🆕 【小程序】增加同城配送相关接口,同时为WxMaService增加了API签名支持

This commit is contained in:
GeXiangDong 2024-11-01 00:14:29 +08:00 committed by GitHub
parent f6e300b10a
commit cff5616463
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
35 changed files with 3632 additions and 580 deletions

Binary file not shown.

After

(image error) Size: 165 KiB

Binary file not shown.

After

(image error) Size: 297 KiB

View File

@ -0,0 +1,46 @@
# 使用API签名
如果对API签名不了解可先阅读微信文档 [服务端API签名指南](https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/getting_started/api_signature.html)
有API数据加密与签名两种功能此处按照微信文档简称为签名。
## 程序内设置
[WxMaConfig](src/main/java/cn/binarywang/wx/miniapp/config/WxMaConfig.java)类增加了几个属性,分别对应小程序内设置的签名密钥等。
* apiSignatureAesKey
* apiSignatureAesKeySn
* apiSignatureRsaPrivateKey
* apiSignatureRsaPrivateKeySn
这4个属性需要按照小程序后台设置。
## 小程序后台设置/查看签名用密钥
在小程序后台,开发管理 -> 开发设置 -> API安全处可以看到如下图界面。
![图一](../images/api-signature/api-signature-1.png)
上图中A处对应 apiSignatureAesKeySn B处对应apiSignatureAesKey; C处对应apiSignatureRsaPrivateKeySn
apiSignatureRsaPrivateKey 在上图中**无**对应C处右侧是公钥apiSignatureRsaPrivateKey 需要的是私钥。
可点击图上右上角的修改,打开如下图的设置页面
![图二](../images/api-signature/api-signature-2.png)
首先确保对称密钥选中 AES256非对称密钥选中RSA。不要选SM4和SM2。
如果需要支持SM4/SM2可修改BaseWxMaServiceImpl.java中postWithSignature方法中相应部分实现
在API非对称密钥中下方左侧有个「随机生成密钥对」点击它然后点它右侧的「下载私钥」之后点击「确认」保存。
下载得到的文件是PKCS1格式的私钥用openssl可转成PKCS8格式。apiSignatureRsaPrivateKey 需要设置的是PKCS8格式的私钥。
注意:
1. 如果不先点击「随机生成密钥对」,直接点击「下载私钥」得到的是公钥,公钥在这里没有用途。
2. 打开下载的文件,第一行是「-----BEGIN RSA PRIVATE KEY-----」说明是PKCS1格式私钥。
3. PKCS8格式第一行是「-----BEGIN PRIVATE KEY-----」
4. 转换命令 `openssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in PKCS1格式密钥文件名 -out 新的PKCS8格式密钥文件名`
5. 如果密钥文件有 PUBLIC KEY 字样,说明下载了公钥,重新生成密钥对,下载私钥

View File

@ -0,0 +1,86 @@
package cn.binarywang.wx.miniapp.api;
import cn.binarywang.wx.miniapp.bean.intractiy.*;
import java.util.List;
import me.chanjar.weixin.common.error.WxErrorException;
/**
* 微信小程序 物流服务 同城配送服务API <br>
* *不是*即时配送接口两个相近容易混淆<br>
* 微信相关接口 <br>
* https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/industry/express/business/intracity_service.html
*/
public interface WxMaIntracityService {
/** 申请开通门店权限 */
void apply() throws WxErrorException;
/** 创建门店 */
String createStore(WxMaStore store) throws WxErrorException;
/**
* 更新门店只更新store中不为null的部分 wxStoreId和outStoreId至少要有一个不为null根据这2个来更新 仅支持更新 storeName orderPattern
* serviceTransPrefer addressInfo几个属性
*/
void updateStore(WxMaStore store) throws WxErrorException;
/** 查询门店(列出所有门店) */
List<WxMaStore> listAllStores() throws WxErrorException;
/** 根据wx_store_id查询门店 */
WxMaStore queryStoreByWxStoreId(String wxStoreId) throws WxErrorException;
/** 根据 out_store_id 查询门店 */
List<WxMaStore> queryStoreByOutStoreId(String outStoreId) throws WxErrorException;
/** 门店运费充值返回充值URL */
String storeCharge(WxMaStoreChargeRequest request) throws WxErrorException;
/** 门店运费退款,返回退款金额 */
int storeRefund(WxMaStoreRefundRequest request) throws WxErrorException;
/** 门店运费流水查询 */
WxMaStoreFlowResponse<? extends WxMaStoreFlowResponse.BasicFlowRecord> queryFlow(
WxMaQueryFlowRequest request) throws WxErrorException;
/** 查询门店余额 */
WxMaStoreBalance balanceQuery(String wxStoreId, String serviceTransId, PayMode payMode)
throws WxErrorException;
/**
* 设置扣费主体 <br>
* 接口调用成功后小程序的管理员会收到模板消息点击模板消息确认更改门店扣费主体后修改生效
*/
void setPayMode(PayMode payMode) throws WxErrorException;
/** 查询扣费主体 */
WxMaGetPayModeResponse getPayMode() throws WxErrorException;
/** 查询运费 */
WxMaAddOrderResponse preAddOrder(WxMaPreAddOrderRequest request) throws WxErrorException;
/** 创建配送单 */
WxMaAddOrderResponse addOrder(WxMaAddOrderRequest order) throws WxErrorException;
/** 查询配送单 根据wxOrderId */
WxMaOrder queryOrderByWxOrderId(String wxOrderId) throws WxErrorException;
/** 依据商户订单号 查询配送单 */
WxMaOrder queryOrderByStoreOrderId(String wxStoreId, String storeOrderId) throws WxErrorException;
/** 依据微信订单号 查询配送单 */
WxMaCancelOrderResponse cancelOrderByWxOrderId(
String wxOrderId, int cancelReasonId, String cancelReason) throws WxErrorException;
/** 依据商户订单号 查询配送单 */
WxMaCancelOrderResponse cancelOrderByStoreOrderId(
String wxStoreId, String storeOrderId, int cancelReasonId, String cancelReason)
throws WxErrorException;
/**
* 查询支持同城配送的城市
*
* @param serviceTransId 运力ID传NULL则返回所有
*/
List<WxMaTransCity> getCity(String serviceTransId) throws WxErrorException;
}

View File

@ -1,7 +1,11 @@
package cn.binarywang.wx.miniapp.api;
import cn.binarywang.wx.miniapp.bean.WxMaApiResponse;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.binarywang.wx.miniapp.config.WxMaConfig;
import cn.binarywang.wx.miniapp.executor.ApiSignaturePostRequestExecutor;
import com.google.gson.JsonObject;
import java.util.Map;
import java.util.function.Function;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.service.WxImgProcService;
@ -11,33 +15,25 @@ import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor;
import me.chanjar.weixin.common.util.http.RequestExecutor;
import me.chanjar.weixin.common.util.http.RequestHttp;
import java.util.Map;
/**
* The interface Wx ma service.
*
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public interface WxMaService extends WxService {
/**
* 获取access_token.
*/
String GET_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s";
/** 获取access_token. */
String GET_ACCESS_TOKEN_URL =
"https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s";
String GET_STABLE_ACCESS_TOKEN = "https://api.weixin.qq.com/cgi-bin/stable_token";
/**
* The constant JSCODE_TO_SESSION_URL.
*/
/** The constant JSCODE_TO_SESSION_URL. */
String JSCODE_TO_SESSION_URL = "https://api.weixin.qq.com/sns/jscode2session";
/**
* getPaidUnionId
*/
/** getPaidUnionId */
String GET_PAID_UNION_ID_URL = "https://api.weixin.qq.com/wxa/getpaidunionid";
/**
* 导入抽样数据
*/
/** 导入抽样数据 */
String SET_DYNAMIC_DATA_URL = "https://api.weixin.qq.com/wxa/setdynamicdata";
/**
@ -51,6 +47,7 @@ public interface WxMaService extends WxService {
/**
* 导入抽样数据
*
* <pre>
* 第三方通过调用微信API将数据写入到setdynamicdata这个API每个Post数据包不超过5K若数据过多可开多进线程并发导入数据例如数据量为十万量级可以开50个线程并行导数据
* 文档地址https://wsad.weixin.qq.com/wsad/zh_CN/htmledition/widget-docs-v3/html/custom/quickstart/implement/import/index.html
@ -58,21 +55,23 @@ public interface WxMaService extends WxService {
* </pre>
*
* @param lifespan 数据有效时间秒为单位一般为86400一天一次导入的频率
* @param type 用于标识数据所属的服务类目
* @param scene 1代表用于搜索的数据
* @param data 推送到微信后台的数据列表该数据被微信用于流量分配注意该字段为string类型而不是object
* @param type 用于标识数据所属的服务类目
* @param scene 1代表用于搜索的数据
* @param data 推送到微信后台的数据列表该数据被微信用于流量分配注意该字段为string类型而不是object
* @throws WxErrorException .
*/
void setDynamicData(int lifespan, String type, int scene, String data) throws WxErrorException;
/**
*
*
* <pre>
* 验证消息的确来自微信服务器.
* 详情请见: http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421135319&token=&lang=zh_CN
* </pre>
*
* @param timestamp the timestamp
* @param nonce the nonce
* @param nonce the nonce
* @param signature the signature
* @return the boolean
*/
@ -88,6 +87,8 @@ public interface WxMaService extends WxService {
String getAccessToken() throws WxErrorException;
/**
*
*
* <pre>
* 获取access_token本方法线程安全.
* 且在多线程同时刷新时只刷新一次避免超出2000次/日的调用次数上限
@ -106,6 +107,8 @@ public interface WxMaService extends WxService {
String getAccessToken(boolean forceRefresh) throws WxErrorException;
/**
*
*
* <pre>
* 用户支付完成后获取该用户的 UnionId无需用户授权本接口支持第三方平台代理查询
*
@ -114,33 +117,45 @@ public interface WxMaService extends WxService {
* 文档地址https://developers.weixin.qq.com/miniprogram/dev/api/getPaidUnionId.html
* </pre>
*
* @param openid 必填 支付用户唯一标识
* @param openid 必填 支付用户唯一标识
* @param transactionId 非必填 微信支付订单号
* @param mchId 非必填 微信支付分配的商户号和商户订单号配合使用
* @param outTradeNo 非必填 微信支付商户订单号和商户号配合使用
* @param mchId 非必填 微信支付分配的商户号和商户订单号配合使用
* @param outTradeNo 非必填 微信支付商户订单号和商户号配合使用
* @return UnionId. paid union id
* @throws WxErrorException .
*/
String getPaidUnionId(String openid, String transactionId, String mchId, String outTradeNo) throws WxErrorException;
String getPaidUnionId(String openid, String transactionId, String mchId, String outTradeNo)
throws WxErrorException;
/**
*
*
* <pre>
* Service没有实现某个API的时候可以用这个
* {@link #get}{@link #post}方法更灵活可以自己构造RequestExecutor用来处理不同的参数和不同的返回类型
* 可以参考{@link MediaUploadRequestExecutor}的实现方法
* </pre>
*
* @param <T> .
* @param <E> .
* @param <T> .
* @param <E> .
* @param executor 执行器
* @param uri 接口请求地址
* @param data 参数或请求数据
* @param uri 接口请求地址
* @param data 参数或请求数据
* @return . t
* @throws WxErrorException the wx error exception
*/
<T, E> T execute(RequestExecutor<T, E> executor, String uri, E data) throws WxErrorException;
WxMaApiResponse execute(
ApiSignaturePostRequestExecutor executor,
String uri,
Map<String, String> headers,
String data)
throws WxErrorException;
/**
*
*
* <pre>
* 设置当微信系统响应系统繁忙时要等待多少 retrySleepMillis(ms) * 2^(重试次数 - 1) 再发起重试.
* 默认1000ms
@ -151,6 +166,8 @@ public interface WxMaService extends WxService {
void setRetrySleepMillis(int retrySleepMillis);
/**
*
*
* <pre>
* 设置当微信系统响应系统繁忙时最大重试次数.
* 默认5次
@ -177,7 +194,7 @@ public interface WxMaService extends WxService {
/**
* Map里 加入新的 {@link WxMaConfig}适用于动态添加新的微信公众号配置.
*
* @param miniappId 小程序标识
* @param miniappId 小程序标识
* @param configStorage 新的微信配置
*/
void addConfig(String miniappId, WxMaConfig configStorage);
@ -190,8 +207,8 @@ public interface WxMaService extends WxService {
void removeConfig(String miniappId);
/**
* 注入多个 {@link WxMaConfig} 的实现. 并为每个 {@link WxMaConfig} 赋予不同的 {@link String mpId}
* 随机采用一个{@link String mpId}进行Http初始化操作
* 注入多个 {@link WxMaConfig} 的实现. 并为每个 {@link WxMaConfig} 赋予不同的 {@link String mpId} 随机采用一个{@link
* String mpId}进行Http初始化操作
*
* @param configs WxMaConfig map
*/
@ -200,7 +217,7 @@ public interface WxMaService extends WxService {
/**
* 注入多个 {@link WxMaConfig} 的实现. 并为每个 {@link WxMaConfig} 赋予不同的 {@link String label}
*
* @param configs WxMaConfig map
* @param configs WxMaConfig map
* @param defaultMiniappId 设置一个{@link WxMaConfig} 所对应的{@link String defaultMiniappId}进行Http初始化
*/
void setMultiConfigs(Map<String, WxMaConfig> configs, String defaultMiniappId);
@ -328,9 +345,7 @@ public interface WxMaService extends WxService {
*/
WxMaPluginService getPluginService();
/**
* 初始化http请求对象.
*/
/** 初始化http请求对象. */
void initHttp();
/**
@ -403,7 +418,6 @@ public interface WxMaService extends WxService {
*/
WxMaShopAfterSaleService getShopAfterSaleService();
/**
* 返回小程序交易组件-物流服务接口
*
@ -411,7 +425,6 @@ public interface WxMaService extends WxService {
*/
WxMaShopDeliveryService getShopDeliveryService();
/**
* 返回小程序交易组件-订单服务接口
*
@ -544,18 +557,21 @@ public interface WxMaService extends WxService {
* @return getWxMaOpenApiService
*/
WxMaOpenApiService getWxMaOpenApiService();
/**
* 小程序短剧管理
*
* @return getWxMaVodService
*/
WxMaVodService getWxMaVodService();
/**
* 小程序虚拟支付
*
* @return getWxMaXPayService
*/
WxMaXPayService getWxMaXPayService();
WxMaExpressDeliveryReturnService getWxMaExpressDeliveryReturnService();
/**
@ -564,4 +580,14 @@ public interface WxMaService extends WxService {
* @return WxMaPromotionService
*/
WxMaPromotionService getWxMaPromotionService();
String postWithSignature(String url, Object obj) throws WxErrorException;
String postWithSignature(String url, JsonObject jsonObject) throws WxErrorException;
/**
* 微信物流服务 -- 同城配送
* https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/industry/express/business/intracity_service.html
*/
WxMaIntracityService getIntracityService();
}

View File

@ -1,16 +1,35 @@
package cn.binarywang.wx.miniapp.api.impl;
import cn.binarywang.wx.miniapp.api.*;
import cn.binarywang.wx.miniapp.bean.WxMaApiResponse;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.binarywang.wx.miniapp.config.WxMaConfig;
import cn.binarywang.wx.miniapp.executor.ApiSignaturePostRequestExecutor;
import cn.binarywang.wx.miniapp.util.WxMaConfigHolder;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.google.gson.FieldNamingPolicy;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.SecureRandom;
import java.security.Signature;
import java.security.interfaces.RSAPrivateKey;
import java.security.spec.MGF1ParameterSpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.PSSParameterSpec;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.function.Function;
import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.common.bean.CommonUploadParam;
@ -25,26 +44,65 @@ import me.chanjar.weixin.common.service.WxImgProcService;
import me.chanjar.weixin.common.service.WxOcrService;
import me.chanjar.weixin.common.util.DataUtils;
import me.chanjar.weixin.common.util.crypto.SHA1;
import me.chanjar.weixin.common.util.http.RequestExecutor;
import me.chanjar.weixin.common.util.http.RequestHttp;
import me.chanjar.weixin.common.util.http.SimpleGetRequestExecutor;
import me.chanjar.weixin.common.util.http.SimplePostRequestExecutor;
import me.chanjar.weixin.common.util.http.*;
import me.chanjar.weixin.common.util.json.GsonParser;
import me.chanjar.weixin.common.util.json.WxGsonBuilder;
import org.apache.commons.lang3.StringUtils;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
/**
* @author <a href="https://github.com/binarywang">Binary Wang</a>
* @see #doGetAccessTokenRequest
*/
@Slf4j
public abstract class BaseWxMaServiceImpl<H, P> implements WxMaService, RequestHttp<H, P> {
/**
* 开启API签名验证后需要API签名的接口根据 https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/
* 整理uri包含下这些字符串且配置了api signature aes ras key 自动用签名接口
*/
protected static final String[] urlPathSupportApiSignature =
new String[] {
"cgi-bin/clear_quota",
"cgi-bin/openapi/quota/get",
"cgi-bin/openapi/rid/get",
"wxa/getpluginopenpid",
"wxa/business/checkencryptedmsg",
"wxa/business/getuserencryptkey",
"wxa/business/getuserphonenumber",
"wxa/getwxacode",
"wxa/getwxacodeunlimit",
"cgi-bin/wxaapp/createwxaqrcode",
"cgi-bin/message/custom/send",
"cgi-bin/message/wxopen/updatablemsg/send",
"wxaapi/newtmpl/deltemplate",
"cgi-bin/message/subscribe/send",
"wxaapi/newtmpl/addtemplate",
"wxa/msg_sec_check",
"wxa/media_check_async",
"wxa/getuserriskrank",
"datacube/getweanalysisappidweeklyretaininfo",
"datacube/getweanalysisappidmonthlyretaininfo",
"datacube/getweanalysisappiddailyretaininfo",
"datacube/getweanalysisappidmonthlyvisittrend",
"datacube/getweanalysisappiddailyvisittrend",
"datacube/getweanalysisappidweeklyvisittrend",
"datacube/getweanalysisappiddailysummarytrend",
"datacube/getweanalysisappidvisitpage",
"datacube/getweanalysisappiduserportrait",
"wxa/business/performance/boot",
"datacube/getweanalysisappidvisitdistribution",
"wxa/getwxadevinfo",
"wxaapi/log/get_performance",
"wxaapi/log/jserr_detail",
"wxaapi/log/jserr_list",
"wxa/devplugin",
"wxa/plugin",
"cgi-bin/express/business/account/getall",
"cgi-bin/express/business/delivery/getall",
"cgi-bin/express/business/printer/getall",
"wxa/servicemarket",
"cgi-bin/soter/verify_signature"
};
protected static final Gson GSON = new Gson();
private final WxMaMsgService kefuService = new WxMaMsgServiceImpl(this);
private final WxMaMediaService materialService = new WxMaMediaServiceImpl(this);
@ -75,26 +133,33 @@ public abstract class BaseWxMaServiceImpl<H, P> implements WxMaService, RequestH
private final WxMaShopCatService shopCatService = new WxMaShopCatServiceImpl(this);
private final WxMaShopImgService shopImgService = new WxMaShopImgServiceImpl(this);
private final WxMaShopAuditService shopAuditService = new WxMaShopAuditServiceImpl(this);
private final WxMaShopAfterSaleService shopAfterSaleService = new WxMaShopAfterSaleServiceImpl(this);
private final WxMaShopAfterSaleService shopAfterSaleService =
new WxMaShopAfterSaleServiceImpl(this);
private final WxMaShopDeliveryService shopDeliveryService = new WxMaShopDeliveryServiceImpl(this);
private final WxMaLinkService linkService = new WxMaLinkServiceImpl(this);
private final WxMaReimburseInvoiceService reimburseInvoiceService = new WxMaReimburseInvoiceServiceImpl(this);
private final WxMaDeviceSubscribeService deviceSubscribeService = new WxMaDeviceSubscribeServiceImpl(this);
private final WxMaReimburseInvoiceService reimburseInvoiceService =
new WxMaReimburseInvoiceServiceImpl(this);
private final WxMaDeviceSubscribeService deviceSubscribeService =
new WxMaDeviceSubscribeServiceImpl(this);
private final WxMaMarketingService marketingService = new WxMaMarketingServiceImpl(this);
private final WxMaImmediateDeliveryService immediateDeliveryService = new WxMaImmediateDeliveryServiceImpl(this);
private final WxMaImmediateDeliveryService immediateDeliveryService =
new WxMaImmediateDeliveryServiceImpl(this);
private final WxMaShopSharerService shopSharerService = new WxMaShopSharerServiceImpl(this);
private final WxMaProductService productService = new WxMaProductServiceImpl(this);
private final WxMaProductOrderService productOrderService = new WxMaProductOrderServiceImpl(this);
private final WxMaShopCouponService wxMaShopCouponService = new WxMaShopCouponServiceImpl(this);
private final WxMaShopPayService wxMaShopPayService = new WxMaShopPayServiceImpl(this);
private final WxMaOrderShippingService wxMaOrderShippingService = new WxMaOrderShippingServiceImpl(this);
private final WxMaOrderShippingService wxMaOrderShippingService =
new WxMaOrderShippingServiceImpl(this);
private final WxMaOpenApiService wxMaOpenApiService = new WxMaOpenApiServiceImpl(this);
private final WxMaVodService wxMaVodService = new WxMaVodServiceImpl(this);
private final WxMaXPayService wxMaXPayService = new WxMaXPayServiceImpl(this);
private final WxMaExpressDeliveryReturnService wxMaExpressDeliveryReturnService = new WxMaExpressDeliveryReturnServiceImpl(this);
private final WxMaExpressDeliveryReturnService wxMaExpressDeliveryReturnService =
new WxMaExpressDeliveryReturnServiceImpl(this);
private final WxMaPromotionService wxMaPromotionService = new WxMaPromotionServiceImpl(this);
private final WxMaIntracityService intracityService = new WxMaIntracityServiceImpl(this);
private Map<String, WxMaConfig> configMap = new HashMap<>();
private int retrySleepMillis = 1000;
@ -107,7 +172,7 @@ public abstract class BaseWxMaServiceImpl<H, P> implements WxMaService, RequestH
@Override
public String getPaidUnionId(String openid, String transactionId, String mchId, String outTradeNo)
throws WxErrorException {
throws WxErrorException {
Map<String, String> params = new HashMap<>(8);
params.put("openid", openid);
@ -123,7 +188,8 @@ public abstract class BaseWxMaServiceImpl<H, P> implements WxMaService, RequestH
params.put("out_trade_no", outTradeNo);
}
String responseContent = this.get(GET_PAID_UNION_ID_URL, Joiner.on("&").withKeyValueSeparator("=").join(params));
String responseContent =
this.get(GET_PAID_UNION_ID_URL, Joiner.on("&").withKeyValueSeparator("=").join(params));
WxError error = WxError.fromJson(responseContent, WxType.MiniApp);
if (error.getErrorCode() != 0) {
throw new WxErrorException(error);
@ -141,12 +207,14 @@ public abstract class BaseWxMaServiceImpl<H, P> implements WxMaService, RequestH
params.put("js_code", jsCode);
params.put("grant_type", "authorization_code");
String result = get(JSCODE_TO_SESSION_URL, Joiner.on("&").withKeyValueSeparator("=").join(params));
String result =
get(JSCODE_TO_SESSION_URL, Joiner.on("&").withKeyValueSeparator("=").join(params));
return WxMaJscode2SessionResult.fromJson(result);
}
@Override
public void setDynamicData(int lifespan, String type, int scene, String data) throws WxErrorException {
public void setDynamicData(int lifespan, String type, int scene, String data)
throws WxErrorException {
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("lifespan", lifespan);
jsonObject.addProperty("query", WxGsonBuilder.create().toJson(ImmutableMap.of("type", type)));
@ -211,7 +279,6 @@ public abstract class BaseWxMaServiceImpl<H, P> implements WxMaService, RequestH
*/
protected abstract String doGetAccessTokenRequest() throws IOException;
/**
* 通过网络请求获取稳定版接口调用凭据
*
@ -225,14 +292,33 @@ public abstract class BaseWxMaServiceImpl<H, P> implements WxMaService, RequestH
return execute(SimpleGetRequestExecutor.create(this), url, queryParam);
}
private boolean isApiSignatureRequired(String url) {
return this.getWxMaConfig().getApiSignatureAesKey() != null
&& Arrays.stream(urlPathSupportApiSignature).anyMatch(part -> url.contains(part));
}
@Override
public String post(String url, String postData) throws WxErrorException {
return execute(SimplePostRequestExecutor.create(this), url, postData);
if (isApiSignatureRequired(url)) {
// 接口需要签名
log.debug("已经配置接口需要签名,接口{}将走加密访问路径", url);
JsonObject jsonObject = GSON.fromJson(postData, JsonObject.class);
return postWithSignature(url, jsonObject);
} else {
return execute(SimplePostRequestExecutor.create(this), url, postData);
}
}
@Override
public String post(String url, Object obj) throws WxErrorException {
return this.execute(SimplePostRequestExecutor.create(this), url, WxGsonBuilder.create().toJson(obj));
if (isApiSignatureRequired(url)) {
// 接口需要签名
log.debug("已经配置接口需要签名,接口{}将走加密访问路径", url);
return postWithSignature(url, obj);
} else {
return this.execute(
SimplePostRequestExecutor.create(this), url, WxGsonBuilder.create().toJson(obj));
}
}
@Override
@ -240,34 +326,67 @@ public abstract class BaseWxMaServiceImpl<H, P> implements WxMaService, RequestH
return this.post(url, obj.toJson());
}
@Override
public String upload(String url, CommonUploadParam param) throws WxErrorException {
RequestExecutor<String, CommonUploadParam> executor = CommonUploadRequestExecutor.create(getRequestHttp());
return this.execute(executor, url, param);
}
@Override
public String post(String url, JsonObject jsonObject) throws WxErrorException {
return this.post(url, jsonObject.toString());
}
/**
* 向微信端发送请求在这里执行的策略是当发生access_token过期时才去刷新然后重新执行请求而不是全局定时请求
*/
@Override
public <T, E> T execute(RequestExecutor<T, E> executor, String uri, E data) throws WxErrorException {
public String upload(String url, CommonUploadParam param) throws WxErrorException {
RequestExecutor<String, CommonUploadParam> executor =
CommonUploadRequestExecutor.create(getRequestHttp());
return this.execute(executor, url, param);
}
/** 向微信端发送请求在这里执行的策略是当发生access_token过期时才去刷新然后重新执行请求而不是全局定时请求 */
@Override
public <R, T> R execute(RequestExecutor<R, T> executor, String uri, T data)
throws WxErrorException {
String dataForLog;
if (data instanceof String) {
dataForLog = DataUtils.handleDataWithSecret((String) data);
} else {
dataForLog = data.toString();
}
return excuteWithRetry(
(uriWithAccessToken) -> executor.execute(uriWithAccessToken, data, WxType.MiniApp),
uri,
dataForLog);
}
@Override
public WxMaApiResponse execute(
ApiSignaturePostRequestExecutor executor,
String uri,
Map<String, String> headers,
String data)
throws WxErrorException {
String dataForLog = "Headers: " + headers.toString() + " Body: " + data;
return excuteWithRetry(
(uriWithAccessToken) -> executor.execute(uriWithAccessToken, headers, data, WxType.MiniApp),
uri,
dataForLog);
}
private static interface ExecutorAction<R> {
R execute(String urlWithAccessToken) throws IOException, WxErrorException;
}
private <R, T> R excuteWithRetry(ExecutorAction<R> executor, String uri, String dataForLog)
throws WxErrorException {
int retryTimes = 0;
do {
try {
return this.executeInternal(executor, uri, data, false);
return this.executeInternal(executor, uri, dataForLog, false);
} catch (WxErrorException e) {
if (retryTimes + 1 > this.maxRetryTimes) {
log.warn("重试达到最大次数【{}】", maxRetryTimes);
//最后一次重试失败后直接抛出异常不再等待
throw new WxErrorException(WxError.builder()
.errorCode(e.getError().getErrorCode())
.errorMsg("微信服务端异常,超出重试次数!")
.build());
// 最后一次重试失败后直接抛出异常不再等待
throw new WxErrorException(
WxError.builder()
.errorCode(e.getError().getErrorCode())
.errorMsg("微信服务端异常,超出重试次数!")
.build());
}
WxError error = e.getError();
@ -290,8 +409,9 @@ public abstract class BaseWxMaServiceImpl<H, P> implements WxMaService, RequestH
throw new WxRuntimeException("微信服务端异常,超出重试次数");
}
private <T, E> T executeInternal(RequestExecutor<T, E> executor, String uri, E data, boolean doNotAutoRefreshToken) throws WxErrorException {
E dataForLog = DataUtils.handleDataWithSecret(data);
private <R, T> R executeInternal(
ExecutorAction<R> executor, String uri, String dataForLog, boolean doNotAutoRefreshToken)
throws WxErrorException {
if (uri.contains("access_token=")) {
throw new IllegalArgumentException("uri参数中不允许有access_token: " + uri);
@ -302,10 +422,10 @@ public abstract class BaseWxMaServiceImpl<H, P> implements WxMaService, RequestH
uri = uri.replace("https://api.weixin.qq.com", this.getWxMaConfig().getApiHostUrl());
}
String uriWithAccessToken = uri + (uri.contains("?") ? "&" : "?") + "access_token=" + accessToken;
String uriWithAccessToken =
uri + (uri.contains("?") ? "&" : "?") + "access_token=" + accessToken;
try {
T result = executor.execute(uriWithAccessToken, data, WxType.MiniApp);
R result = executor.execute(uriWithAccessToken);
log.debug("\n【请求地址】: {}\n【请求参数】{}\n【响应数据】{}", uriWithAccessToken, dataForLog, result);
return result;
} catch (WxErrorException e) {
@ -324,10 +444,11 @@ public abstract class BaseWxMaServiceImpl<H, P> implements WxMaService, RequestH
lock.unlock();
}
if (this.getWxMaConfig().autoRefreshToken() && !doNotAutoRefreshToken) {
log.warn("即将重新获取新的access_token错误代码{},错误信息:{}", error.getErrorCode(), error.getErrorMsg());
//下一次不再自动重试
//当小程序误调用第三方平台专属接口时,第三方无法使用小程序的access token,如果可以继续自动获取token会导致无限循环重试,直到栈溢出
return this.executeInternal(executor, uri, data, true);
log.warn(
"即将重新获取新的access_token错误代码{},错误信息:{}", error.getErrorCode(), error.getErrorMsg());
// 下一次不再自动重试
// 当小程序误调用第三方平台专属接口时,第三方无法使用小程序的access token,如果可以继续自动获取token会导致无限循环重试,直到栈溢出
return this.executeInternal(executor, uri, dataForLog, true);
}
}
@ -337,7 +458,8 @@ public abstract class BaseWxMaServiceImpl<H, P> implements WxMaService, RequestH
}
return null;
} catch (IOException e) {
log.warn("\n【请求地址】: {}\n【请求参数】{}\n【异常信息】{}", uriWithAccessToken, dataForLog, e.getMessage());
log.warn(
"\n【请求地址】: {}\n【请求参数】{}\n【异常信息】{}", uriWithAccessToken, dataForLog, e.getMessage());
throw new WxRuntimeException(e);
}
}
@ -712,6 +834,164 @@ public abstract class BaseWxMaServiceImpl<H, P> implements WxMaService, RequestH
@Override
public WxMaPromotionService getWxMaPromotionService() {
return this.wxMaPromotionService;
return this.wxMaPromotionService;
}
@Override
public String postWithSignature(String url, Object obj) throws WxErrorException {
Gson gson =
new GsonBuilder()
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
.create();
JsonObject jsonObject = gson.toJsonTree(obj).getAsJsonObject();
return this.postWithSignature(url, jsonObject);
}
private String generateNonce() {
byte[] nonce = generateRandomBytes(16);
return base64Encode(nonce).replace("=", "");
}
private byte[] generateRandomBytes(int length) {
byte[] bytes = new byte[length];
new SecureRandom().nextBytes(bytes);
return bytes;
}
private String base64Encode(byte[] data) {
return Base64.getEncoder().encodeToString(data);
}
@Override
public String postWithSignature(String url, JsonObject jsonObject) throws WxErrorException {
long timestamp = System.currentTimeMillis() / 1000;
String appId = this.getWxMaConfig().getWechatMpAppid();
String rndStr = UUID.randomUUID().toString().replace("-", "").substring(0, 30);
String aesKey = this.getWxMaConfig().getApiSignatureAesKey();
String aesKeySn = this.getWxMaConfig().getApiSignatureAesKeySn();
jsonObject.addProperty("_n", rndStr);
jsonObject.addProperty("_appid", appId);
jsonObject.addProperty("_timestamp", timestamp);
String plainText = jsonObject.toString();
String urlPath;
if (url.contains("?")) {
urlPath = url.substring(0, url.indexOf("?"));
} else {
urlPath = url;
}
String aad = urlPath + "|" + appId + "|" + timestamp + "|" + aesKeySn;
byte[] realKey;
try {
realKey = Base64.getDecoder().decode(aesKey);
} catch (Exception ex) {
log.error("解析AESKEY失败 {}", aesKey, ex);
throw new SecurityException("解析AES KEY失败请检查ApiSignatureAesKey是否正确", ex);
}
byte[] realIv = generateRandomBytes(12);
byte[] realAad = aad.getBytes(StandardCharsets.UTF_8);
byte[] realPlainText = plainText.getBytes(StandardCharsets.UTF_8);
try {
// 加密内容 AES
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
SecretKeySpec aesKeySpec = new SecretKeySpec(realKey, "AES");
GCMParameterSpec parameterSpec = new GCMParameterSpec(128, realIv);
cipher.init(Cipher.ENCRYPT_MODE, aesKeySpec, parameterSpec);
cipher.updateAAD(realAad);
byte[] ciphertext = cipher.doFinal(realPlainText);
byte[] encryptedData = Arrays.copyOfRange(ciphertext, 0, ciphertext.length - 16);
byte[] authTag = Arrays.copyOfRange(ciphertext, ciphertext.length - 16, ciphertext.length);
JsonObject reqData = new JsonObject();
reqData.addProperty("iv", base64Encode(realIv));
reqData.addProperty("data", base64Encode(encryptedData));
reqData.addProperty("authtag", base64Encode(authTag));
String requestJson = reqData.toString();
// 计算签名 RSA
String payload = urlPath + "\n" + appId + "\n" + timestamp + "\n" + requestJson;
byte[] dataBuffer = payload.getBytes(StandardCharsets.UTF_8);
RSAPrivateKey priKey;
try {
String rsaPrivateKey = this.getWxMaConfig().getApiSignatureRsaPrivateKey();
rsaPrivateKey = rsaPrivateKey.replace("-----BEGIN PRIVATE KEY-----", "");
rsaPrivateKey = rsaPrivateKey.replace("-----END PRIVATE KEY-----", "");
rsaPrivateKey = rsaPrivateKey.replaceAll("\\s+", "");
byte[] decoded = Base64.getDecoder().decode(rsaPrivateKey.getBytes(StandardCharsets.UTF_8));
PKCS8EncodedKeySpec rsaKeySpec = new PKCS8EncodedKeySpec(decoded);
priKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(rsaKeySpec);
} catch (Exception ex) {
log.error("解析RSA KEY失败 {}", aesKey, ex);
throw new SecurityException("解析RSA KEY失败请检查ApiSignatureRsaPrivateKey是否正确需要PKCS8格式私钥", ex);
}
Signature signature = Signature.getInstance("RSASSA-PSS");
// salt长度需与SHA256结果长度(32)一致
PSSParameterSpec pssParameterSpec =
new PSSParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, 32, 1);
signature.setParameter(pssParameterSpec);
signature.initSign(priKey);
signature.update(dataBuffer);
byte[] sigBuffer = signature.sign();
String signatureString = base64Encode(sigBuffer);
Map<String, String> header = new HashMap<>();
header.put("Wechatmp-Signature", signatureString);
header.put("Wechatmp-Appid", appId);
header.put("Wechatmp-TimeStamp", String.valueOf(timestamp));
log.debug("发送请求uri:{}, headers:{}, postData:{}", url, header, requestJson);
WxMaApiResponse response =
this.execute(ApiSignaturePostRequestExecutor.create(this), url, header, requestJson);
String respTs = response.getHeaders().get("Wechatmp-TimeStamp");
String respAad = urlPath + "|" + appId + "|" + respTs + "|" + aesKeySn;
if (!appId.equals(response.getHeaders().get("Wechatmp-Appid"))) {
throw new RuntimeException("响应的appId不符 " + response.getHeaders().get("Wechatmp-Appid"));
}
// 省略验证平台签名部分直接解密内容返回明文
String decryptedData = aesDecodeResponse(response, respAad, aesKeySpec);
log.debug("解密后的响应:{}", decryptedData);
WxError error = WxError.fromJson(decryptedData, WxType.MiniApp);
if (error.getErrorCode() != 0) {
log.debug("调用API出错 uri:{}, postData:{}, response:{}", url, plainText, error);
throw new WxErrorException(error);
}
return decryptedData;
} catch (WxErrorException | SecurityException ex) {
throw ex;
} catch (Exception e) {
log.error("postWithSignature", e);
throw new RuntimeException(e);
}
}
private String aesDecodeResponse(WxMaApiResponse response, String aad, SecretKeySpec aesKeySpec)
throws Exception {
Map<?, ?> map = GSON.fromJson(response.getContent(), Map.class);
String iv = (String) map.get("iv");
String data = (String) map.get("data");
String authTag = (String) map.get("authtag");
byte[] dataBytes = Base64.getDecoder().decode(data);
byte[] authTagBytes = Base64.getDecoder().decode(authTag);
byte[] newDataBytes = new byte[dataBytes.length + authTagBytes.length];
System.arraycopy(dataBytes, 0, newDataBytes, 0, dataBytes.length);
System.arraycopy(authTagBytes, 0, newDataBytes, dataBytes.length, authTagBytes.length);
byte[] aadBytes = aad.getBytes(StandardCharsets.UTF_8);
byte[] ivBytes = Base64.getDecoder().decode(iv);
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(128, ivBytes);
cipher.init(Cipher.DECRYPT_MODE, aesKeySpec, gcmParameterSpec);
cipher.updateAAD(aadBytes);
byte[] decryptedBytes = cipher.doFinal(newDataBytes);
return new String(decryptedBytes, StandardCharsets.UTF_8);
}
@Override
public WxMaIntracityService getIntracityService() {
return this.intracityService;
}
}

View File

@ -0,0 +1,276 @@
package cn.binarywang.wx.miniapp.api.impl;
import static cn.binarywang.wx.miniapp.constant.WxMaApiUrlConstants.Intracity;
import static me.chanjar.weixin.common.api.WxConsts.ERR_CODE;
import cn.binarywang.wx.miniapp.api.WxMaIntracityService;
import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.intractiy.*;
import com.google.gson.*;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.util.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.enums.WxType;
import me.chanjar.weixin.common.error.WxError;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.util.json.GsonParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@RequiredArgsConstructor
@Slf4j
public class WxMaIntracityServiceImpl implements WxMaIntracityService {
private final WxMaService wxMaService;
private static final Logger logger = LoggerFactory.getLogger(WxMaIntracityServiceImpl.class);
private final Gson gson =
new GsonBuilder()
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
.create();
private void checkStringResponse(String response) throws WxErrorException {
JsonObject respObj = GsonParser.parse(response);
if (respObj.get(ERR_CODE).getAsInt() != 0) {
throw new WxErrorException(WxError.fromJson(response, WxType.MiniApp));
}
}
@Override
public void apply() throws WxErrorException {
String response = this.wxMaService.post(Intracity.APPLY_URL, "{}");
checkStringResponse(response);
}
@Override
public String createStore(WxMaStore store) throws WxErrorException {
if (store.getOutStoreId() == null) {
throw new IllegalArgumentException("创建门店时outStoreId不能为空");
}
if (store.getWxStoreId() != null) {
throw new IllegalArgumentException("创建门店时wxStoreId只能是null");
}
String response = this.wxMaService.postWithSignature(Intracity.CREATE_STORE_URL, store);
Map<?, ?> map = gson.fromJson(response, Map.class);
return (String) map.get("wx_store_id");
}
@Override
public void updateStore(WxMaStore store) throws WxErrorException {
if (store.getWxStoreId() == null && store.getOutStoreId() == null) {
throw new IllegalArgumentException("更新门店时wxStoreId 或 outStoreId 至少要有一个不为null");
}
JsonObject request = new JsonObject();
Map<String, String> keys = new HashMap<>();
if (store.getWxStoreId() != null) {
keys.put("wx_store_id", store.getWxStoreId());
} else {
keys.put("out_store_id", store.getOutStoreId());
}
request.add("keys", gson.toJsonTree(keys));
Map<String, Object> updateContent = new HashMap<>();
if (store.getStoreName() != null) {
updateContent.put("store_name", store.getStoreName());
}
if (store.getOrderPattern() == 1 || store.getOrderPattern() == 2) {
updateContent.put("order_pattern", store.getOrderPattern());
}
if (store.getServiceTransPrefer() != null) {
updateContent.put("service_trans_prefer", store.getServiceTransPrefer());
}
if (store.getAddressInfo() != null) {
updateContent.put("address_info", store.getAddressInfo());
}
request.add("content", gson.toJsonTree(updateContent));
String response = this.wxMaService.postWithSignature(Intracity.UPDATE_STORE_URL, request);
checkStringResponse(response);
}
@Override
public List<WxMaStore> listAllStores() throws WxErrorException {
return queryStore(null, null);
}
@Override
public WxMaStore queryStoreByWxStoreId(String wxStoreId) throws WxErrorException {
List<WxMaStore> list = queryStore(wxStoreId, null);
return list.isEmpty() ? null : list.get(0);
}
@Override
public List<WxMaStore> queryStoreByOutStoreId(String outStoreId) throws WxErrorException {
return queryStore(null, outStoreId);
}
private List<WxMaStore> queryStore(String wxStoreId, String outStoreId) throws WxErrorException {
Map<String, String> map = new HashMap<>();
if (wxStoreId != null) {
map.put("wx_store_id", wxStoreId);
} else if (outStoreId != null) {
map.put("out_store_id", outStoreId);
}
String response = this.wxMaService.postWithSignature(Intracity.QUERY_STORE_URL, map);
JsonObject jsonObject = gson.fromJson(response, JsonObject.class);
Type listType = new TypeToken<List<WxMaStore>>() {}.getType();
return gson.fromJson(jsonObject.getAsJsonArray("store_list"), listType);
}
@Override
public String storeCharge(WxMaStoreChargeRequest request) throws WxErrorException {
String response = this.wxMaService.postWithSignature(Intracity.STORE_CHARGE, request);
Map<?, ?> map = gson.fromJson(response, Map.class);
return (String) map.get("payurl");
}
@Override
public int storeRefund(WxMaStoreRefundRequest request) throws WxErrorException {
String response = this.wxMaService.postWithSignature(Intracity.STORE_REFUND, request);
Map<?, ?> map = gson.fromJson(response, Map.class);
return ((Number) map.get("refund_amount")).intValue();
}
@Override
public WxMaStoreFlowResponse<? extends WxMaStoreFlowResponse.BasicFlowRecord> queryFlow(
WxMaQueryFlowRequest request) throws WxErrorException {
if (request == null || request.getWxStoreId() == null) {
throw new IllegalArgumentException("查询请求 wxStoreId 不可为空");
}
WxMaStoreFlowResponse<? extends WxMaStoreFlowResponse.BasicFlowRecord> inst =
getFlowInstanceByType(request.getFlowType());
if (inst == null) {
throw new IllegalArgumentException("查询请求 flowType 不正确只能是1、2、3之一");
}
String response = this.wxMaService.postWithSignature(Intracity.QUERY_FLOW, request);
WxMaStoreFlowResponse<? extends WxMaStoreFlowResponse.BasicFlowRecord> flowResponse;
flowResponse =
(WxMaStoreFlowResponse<? extends WxMaStoreFlowResponse.BasicFlowRecord>)
gson.fromJson(response, inst.getClass());
logger.debug("queryFlow: {}", flowResponse);
return flowResponse;
}
private WxMaStoreFlowResponse<? extends WxMaStoreFlowResponse.BasicFlowRecord>
getFlowInstanceByType(int flowType) {
WxMaStoreFlowResponse<? extends WxMaStoreFlowResponse.BasicFlowRecord> inst;
if (flowType == 1) {
inst = new WxMaStoreFlowResponse<WxMaStoreFlowResponse.ChargeFlowRecord>();
} else if (flowType == 2) {
inst = new WxMaStoreFlowResponse<WxMaStoreFlowResponse.RefundFlowRecord>();
} else if (flowType == 3) {
inst = new WxMaStoreFlowResponse<WxMaStoreFlowResponse.ConsumeFlowRecord>();
} else {
return null;
}
return inst;
}
@Override
public WxMaStoreBalance balanceQuery(String wxStoreId, String serviceTransId, PayMode payMode)
throws WxErrorException {
if (wxStoreId == null && (payMode != null && payMode != PayMode.STORE)) {
throw new IllegalArgumentException("payMode是PAY_MODE_STORE或null时必须传递wxStoreId");
}
Map<String, Object> request = new HashMap<>();
if (wxStoreId != null) {
request.put("wx_store_id", wxStoreId);
}
if (serviceTransId != null) {
request.put("service_trans_id", serviceTransId);
}
if (payMode != null) {
request.put("pay_mode", payMode);
}
String response = this.wxMaService.postWithSignature(Intracity.BALANCE_QUERY, request);
WxMaStoreBalance balance = gson.fromJson(response, WxMaStoreBalance.class);
logger.debug("balance: {}", balance);
return balance;
}
public void setPayMode(PayMode payMode) throws WxErrorException {
Map<String, Object> request = new HashMap<>();
request.put("pay_mode", payMode);
request.put("appid", wxMaService.getWxMaConfig().getAppid());
String response = this.wxMaService.postWithSignature(Intracity.SET_PAY_MODE, request);
checkStringResponse(response);
}
public WxMaGetPayModeResponse getPayMode() throws WxErrorException {
Map<String, Object> request = new HashMap<>();
request.put("appid", wxMaService.getWxMaConfig().getAppid());
String response = this.wxMaService.postWithSignature(Intracity.GET_PAY_MODE, request);
return gson.fromJson(response, WxMaGetPayModeResponse.class);
}
@Override
public WxMaAddOrderResponse preAddOrder(WxMaPreAddOrderRequest request) throws WxErrorException {
String response = this.wxMaService.postWithSignature(Intracity.PRE_ADD_ORDER, request);
return gson.fromJson(response, WxMaAddOrderResponse.class);
}
@Override
public WxMaAddOrderResponse addOrder(WxMaAddOrderRequest request) throws WxErrorException {
String response = this.wxMaService.postWithSignature(Intracity.ADD_ORDER, request);
return gson.fromJson(response, WxMaAddOrderResponse.class);
}
@Override
public WxMaOrder queryOrderByWxOrderId(String wxOrderId) throws WxErrorException {
Map<String, Object> map = new HashMap<>();
map.put("wx_order_id", wxOrderId);
String response = this.wxMaService.postWithSignature(Intracity.QUERY_ORDER, map);
return gson.fromJson(response, WxMaOrder.class);
}
@Override
public WxMaOrder queryOrderByStoreOrderId(String wxStoreId, String storeOrderId)
throws WxErrorException {
Map<String, Object> map = new HashMap<>();
map.put("wx_store_id", wxStoreId);
map.put("store_order_id", storeOrderId);
String response = this.wxMaService.postWithSignature(Intracity.QUERY_ORDER, map);
return gson.fromJson(response, WxMaOrder.class);
}
@Override
public WxMaCancelOrderResponse cancelOrderByWxOrderId(
String wxOrderId, int cancelReasonId, String cancelReason) throws WxErrorException {
Map<String, Object> map = new HashMap<>();
map.put("wx_order_id", wxOrderId);
map.put("cancel_reason_id", cancelReasonId);
if (cancelReason != null) {
map.put("cancel_reason", cancelReason);
}
String response = this.wxMaService.postWithSignature(Intracity.CANCEL_ORDER, map);
return gson.fromJson(response, WxMaCancelOrderResponse.class);
}
@Override
public WxMaCancelOrderResponse cancelOrderByStoreOrderId(
String wxStoreId, String storeOrderId, int cancelReasonId, String cancelReason)
throws WxErrorException {
Map<String, Object> map = new HashMap<>();
map.put("wx_store_id", wxStoreId);
map.put("store_order_id", storeOrderId);
map.put("cancel_reason_id", cancelReasonId);
if (cancelReason != null) {
map.put("cancel_reason", cancelReason);
}
String response = this.wxMaService.postWithSignature(Intracity.CANCEL_ORDER, map);
return gson.fromJson(response, WxMaCancelOrderResponse.class);
}
@Override
public List<WxMaTransCity> getCity(String serviceTransId) throws WxErrorException {
Map<String, Object> map = new HashMap<>();
if (serviceTransId != null) {
map.put("service_trans_id", serviceTransId);
}
String response = this.wxMaService.postWithSignature(Intracity.GET_CITY, map);
JsonObject jsonObject = gson.fromJson(response, JsonObject.class);
Type listType = new TypeToken<List<WxMaTransCity>>() {}.getType();
return gson.fromJson(jsonObject.getAsJsonArray("support_list"), listType);
}
}

View File

@ -0,0 +1,34 @@
package cn.binarywang.wx.miniapp.bean;
import java.util.Map;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class WxMaApiResponse {
private static final Logger logger = LoggerFactory.getLogger(WxMaApiResponse.class);
private String content;
private Map<String, String> headers;
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public Map<String, String> getHeaders() {
return headers;
}
public void setHeaders(Map<String, String> headers) {
this.headers = headers;
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
}

View File

@ -0,0 +1,128 @@
package cn.binarywang.wx.miniapp.bean.intractiy;
import org.apache.commons.lang3.builder.ToStringBuilder;
class BasicWxMaOrder {
private String wxStoreId;
private String userName;
private String userPhone;
private double userLng;
private double userLat;
private String userAddress;
private int useSandbox;
public String getWxStoreId() {
return wxStoreId;
}
public void setWxStoreId(String wxStoreId) {
this.wxStoreId = wxStoreId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getUserPhone() {
return userPhone;
}
public void setUserPhone(String userPhone) {
this.userPhone = userPhone;
}
public double getUserLng() {
return userLng;
}
public void setUserLng(double userLng) {
this.userLng = userLng;
}
public double getUserLat() {
return userLat;
}
public void setUserLat(double userLat) {
this.userLat = userLat;
}
public String getUserAddress() {
return userAddress;
}
public void setUserAddress(String userAddress) {
this.userAddress = userAddress;
}
public int isUseSandbox() {
return useSandbox;
}
public void setUseSandbox(int useSandbox) {
this.useSandbox = useSandbox;
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
static class Cargo {
private String cargoName;
private int cargoWeight;
private int cargoType;
private int cargoNum;
private int cargoPrice;
public String getCargoName() {
return cargoName;
}
public void setCargoName(String cargoName) {
this.cargoName = cargoName;
}
public int getCargoWeight() {
return cargoWeight;
}
public void setCargoWeight(int cargoWeight) {
this.cargoWeight = cargoWeight;
}
public int getCargoType() {
return cargoType;
}
public void setCargoType(int cargoType) {
this.cargoType = cargoType;
}
public int getCargoNum() {
return cargoNum;
}
public void setCargoNum(int cargoNum) {
this.cargoNum = cargoNum;
}
public int getCargoPrice() {
return cargoPrice;
}
public void setCargoPrice(int cargoPrice) {
this.cargoPrice = cargoPrice;
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
}
}

View File

@ -0,0 +1,49 @@
package cn.binarywang.wx.miniapp.bean.intractiy;
import org.apache.commons.lang3.builder.ToStringBuilder;
class BasicWxMaStoreChargeRefundRequest {
/** 微信门店编号 pay_mode = PAY_MODE_STORE时必传不传pay_mode时必传wx_store_id */
private String wxStoreId;
/**
* 充值/扣费主体 <br>
* 门店PAY_MODE_STORE小程序:PAY_MODE_APP服务商PAY_MODE_COMPONENT不传pay_mode默认pay_mode=PAY_MODE_STORE
*/
private PayMode payMode;
/**
* 运力Id必填运力ID请参考https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/industry/express/business/intracity_service.html#_6-%E8%BF%90%E5%8A%9B%E5%88%97%E8%A1%A8
*/
private String serviceTransId;
public String getWxStoreId() {
return wxStoreId;
}
public void setWxStoreId(String wxStoreId) {
this.wxStoreId = wxStoreId;
}
public PayMode getPayMode() {
return payMode;
}
public void setPayMode(PayMode payMode) {
this.payMode = payMode;
}
public String getServiceTransId() {
return serviceTransId;
}
public void setServiceTransId(String serviceTransId) {
this.serviceTransId = serviceTransId;
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
}

View File

@ -0,0 +1,16 @@
package cn.binarywang.wx.miniapp.bean.intractiy;
import com.google.gson.annotations.SerializedName;
/** 充值、扣费主体 */
public enum PayMode {
/** 门店 */
@SerializedName("PAY_MODE_STORE")
STORE,
/** 小程序 */
@SerializedName("PAY_MODE_APP")
APP,
/** 服务商 */
@SerializedName("PAY_MODE_COMPONENT")
COMPONENT;
}

View File

@ -0,0 +1,133 @@
package cn.binarywang.wx.miniapp.bean.intractiy;
import java.util.List;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class WxMaAddOrderRequest extends BasicWxMaOrder {
private static final Logger logger = LoggerFactory.getLogger(WxMaAddOrderRequest.class);
private String storeOrderId;
private String userOpenid;
private String orderSeq;
/** 验证码类型 0:不生成 1:生成取货码 2:生成收货码 3:两者都生成 */
private int verifyCodeType;
private String orderDetailPath;
private String callbackUrl;
private Cargo cargo;
public String getStoreOrderId() {
return storeOrderId;
}
public void setStoreOrderId(String storeOrderId) {
this.storeOrderId = storeOrderId;
}
public String getUserOpenid() {
return userOpenid;
}
public void setUserOpenid(String userOpenid) {
this.userOpenid = userOpenid;
}
public String getOrderSeq() {
return orderSeq;
}
public void setOrderSeq(String orderSeq) {
this.orderSeq = orderSeq;
}
public int getVerifyCodeType() {
return verifyCodeType;
}
public void setVerifyCodeType(int verifyCodeType) {
this.verifyCodeType = verifyCodeType;
}
public String getOrderDetailPath() {
return orderDetailPath;
}
public void setOrderDetailPath(String orderDetailPath) {
this.orderDetailPath = orderDetailPath;
}
public String getCallbackUrl() {
return callbackUrl;
}
public void setCallbackUrl(String callbackUrl) {
this.callbackUrl = callbackUrl;
}
public Cargo getCargo() {
return cargo;
}
public void setCargo(Cargo cargo) {
this.cargo = cargo;
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
public static class Cargo extends BasicWxMaOrder.Cargo {
private List<ItemDetail> itemList;
public List<ItemDetail> getItemList() {
return itemList;
}
public void setItemList(List<ItemDetail> itemList) {
this.itemList = itemList;
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
}
public static class ItemDetail {
private String itemName;
private String itemPicUrl;
private int count;
public String getItemName() {
return itemName;
}
public void setItemName(String itemName) {
this.itemName = itemName;
}
public String getItemPicUrl() {
return itemPicUrl;
}
public void setItemPicUrl(String itemPicUrl) {
this.itemPicUrl = itemPicUrl;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
}
}

View File

@ -0,0 +1,115 @@
package cn.binarywang.wx.miniapp.bean.intractiy;
import org.apache.commons.lang3.builder.ToStringBuilder;
public class WxMaAddOrderResponse {
private String wxOrderId;
private String storeOrderId;
private String wxStoreId;
/** 配送运力 */
private String serviceTransId;
/** 配送距离 单位:米 */
private int distance;
/** 运力订单号 */
private String transOrderId;
/** 运力配送单号 */
private String waybillId;
/** 配送费 */
private int fee;
/** 取货码 */
private String fetchCode;
/** 取货序号 */
private String orderSeq;
public String getWxOrderId() {
return wxOrderId;
}
public void setWxOrderId(String wxOrderId) {
this.wxOrderId = wxOrderId;
}
public String getStoreOrderId() {
return storeOrderId;
}
public void setStoreOrderId(String storeOrderId) {
this.storeOrderId = storeOrderId;
}
public String getWxStoreId() {
return wxStoreId;
}
public void setWxStoreId(String wxStoreId) {
this.wxStoreId = wxStoreId;
}
public String getServiceTransId() {
return serviceTransId;
}
public void setServiceTransId(String serviceTransId) {
this.serviceTransId = serviceTransId;
}
public int getDistance() {
return distance;
}
public void setDistance(int distance) {
this.distance = distance;
}
public String getTransOrderId() {
return transOrderId;
}
public void setTransOrderId(String transOrderId) {
this.transOrderId = transOrderId;
}
public String getWaybillId() {
return waybillId;
}
public void setWaybillId(String waybillId) {
this.waybillId = waybillId;
}
public int getFee() {
return fee;
}
public void setFee(int fee) {
this.fee = fee;
}
public String getFetchCode() {
return fetchCode;
}
public void setFetchCode(String fetchCode) {
this.fetchCode = fetchCode;
}
public String getOrderSeq() {
return orderSeq;
}
public void setOrderSeq(String orderSeq) {
this.orderSeq = orderSeq;
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
}

View File

@ -0,0 +1,67 @@
package cn.binarywang.wx.miniapp.bean.intractiy;
import org.apache.commons.lang3.builder.ToStringBuilder;
public class WxMaCancelOrderResponse {
private String wxOrderId;
private String storeOrderId;
private String wxStoreId;
private String orderStatus;
private String appid;
/** 违约金 */
private int deductfee;
public String getWxOrderId() {
return wxOrderId;
}
public void setWxOrderId(String wxOrderId) {
this.wxOrderId = wxOrderId;
}
public String getStoreOrderId() {
return storeOrderId;
}
public void setStoreOrderId(String storeOrderId) {
this.storeOrderId = storeOrderId;
}
public String getWxStoreId() {
return wxStoreId;
}
public void setWxStoreId(String wxStoreId) {
this.wxStoreId = wxStoreId;
}
public String getOrderStatus() {
return orderStatus;
}
public void setOrderStatus(String orderStatus) {
this.orderStatus = orderStatus;
}
public String getAppid() {
return appid;
}
public void setAppid(String appid) {
this.appid = appid;
}
public int getDeductfee() {
return deductfee;
}
public void setDeductfee(int deductfee) {
this.deductfee = deductfee;
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
}

View File

@ -0,0 +1,42 @@
package cn.binarywang.wx.miniapp.bean.intractiy;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class WxMaGetPayModeResponse {
private static final Logger logger = LoggerFactory.getLogger(WxMaGetPayModeResponse.class);
private PayMode payMode;
private String payAppid;
private String componentAppid;
public PayMode getPayMode() {
return payMode;
}
public void setPayMode(PayMode payMode) {
this.payMode = payMode;
}
public String getPayAppid() {
return payAppid;
}
public void setPayAppid(String payAppid) {
this.payAppid = payAppid;
}
public String getComponentAppid() {
return componentAppid;
}
public void setComponentAppid(String componentAppid) {
this.componentAppid = componentAppid;
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
}

View File

@ -0,0 +1,344 @@
package cn.binarywang.wx.miniapp.bean.intractiy;
import java.util.Date;
import org.apache.commons.lang3.builder.ToStringBuilder;
public class WxMaOrder extends WxMaAddOrderRequest {
private String wxOrderId;
private int orderStatus;
private String appid;
private String serviceTransId;
private String deliveryNo;
private int actualfee;
private int deductfee;
private long createTime;
private long acceptTime;
private long fetchTime;
private long finishTime;
private long cancelTime;
private long expectedFinishTime;
private String fetchCode;
private String recvCode;
private TransporterInfo transporterInfo;
private StoreInfo storeInfo;
private ReceiverInfo receiverInfo;
private Cargo cargoInfo;
public String getWxOrderId() {
return wxOrderId;
}
public void setWxOrderId(String wxOrderId) {
this.wxOrderId = wxOrderId;
}
public int getOrderStatus() {
return orderStatus;
}
public void setOrderStatus(int orderStatus) {
this.orderStatus = orderStatus;
}
public String getAppid() {
return appid;
}
public void setAppid(String appid) {
this.appid = appid;
}
public String getServiceTransId() {
return serviceTransId;
}
public void setServiceTransId(String serviceTransId) {
this.serviceTransId = serviceTransId;
}
public String getDeliveryNo() {
return deliveryNo;
}
public void setDeliveryNo(String deliveryNo) {
this.deliveryNo = deliveryNo;
}
public int getActualfee() {
return actualfee;
}
public void setActualfee(int actualfee) {
this.actualfee = actualfee;
}
public int getDeductfee() {
return deductfee;
}
public void setDeductfee(int deductfee) {
this.deductfee = deductfee;
}
public long getCreateTime() {
return createTime;
}
public void setCreateTime(long createTime) {
this.createTime = createTime;
}
public long getAcceptTime() {
return acceptTime;
}
public void setAcceptTime(long acceptTime) {
this.acceptTime = acceptTime;
}
public long getFetchTime() {
return fetchTime;
}
public void setFetchTime(long fetchTime) {
this.fetchTime = fetchTime;
}
public long getFinishTime() {
return finishTime;
}
public void setFinishTime(long finishTime) {
this.finishTime = finishTime;
}
public long getCancelTime() {
return cancelTime;
}
public void setCancelTime(long cancelTime) {
this.cancelTime = cancelTime;
}
public long getExpectedFinishTime() {
return expectedFinishTime;
}
public void setExpectedFinishTime(long expectedFinishTime) {
this.expectedFinishTime = expectedFinishTime;
}
public String getFetchCode() {
return fetchCode;
}
public void setFetchCode(String fetchCode) {
this.fetchCode = fetchCode;
}
public String getRecvCode() {
return recvCode;
}
public void setRecvCode(String recvCode) {
this.recvCode = recvCode;
}
public TransporterInfo getTransporterInfo() {
return transporterInfo;
}
public void setTransporterInfo(TransporterInfo transporterInfo) {
this.transporterInfo = transporterInfo;
}
public StoreInfo getStoreInfo() {
return storeInfo;
}
public void setStoreInfo(StoreInfo storeInfo) {
this.storeInfo = storeInfo;
}
public ReceiverInfo getReceiverInfo() {
return receiverInfo;
}
public void setReceiverInfo(ReceiverInfo receiverInfo) {
this.receiverInfo = receiverInfo;
}
public Cargo getCargoInfo() {
return cargoInfo;
}
public void setCargoInfo(Cargo cargoInfo) {
this.cargoInfo = cargoInfo;
}
public Date getCreateDate() {
return createTime == 0 ? null : new Date(createTime * 1000);
}
public Date getAcceptDate() {
return acceptTime == 0 ? null : new Date(acceptTime * 1000);
}
public Date getFetchDate() {
return fetchTime == 0 ? null : new Date(fetchTime * 1000);
}
public Date getFinishDate() {
return finishTime == 0 ? null : new Date(finishTime * 1000);
}
public Date getCancelDate() {
return cancelTime == 0 ? null : new Date(cancelTime * 1000);
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
public static class TransporterInfo {
private String transporterName;
private String transporterPhone;
public String getTransporterName() {
return transporterName;
}
public void setTransporterName(String transporterName) {
this.transporterName = transporterName;
}
public String getTransporterPhone() {
return transporterPhone;
}
public void setTransporterPhone(String transporterPhone) {
this.transporterPhone = transporterPhone;
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
}
public static class StoreInfo {
private String storeName;
private String wxStoreId;
private String address;
private double lng;
private double lat;
private String phoneNum;
public String getStoreName() {
return storeName;
}
public void setStoreName(String storeName) {
this.storeName = storeName;
}
public String getWxStoreId() {
return wxStoreId;
}
public void setWxStoreId(String wxStoreId) {
this.wxStoreId = wxStoreId;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public double getLng() {
return lng;
}
public void setLng(double lng) {
this.lng = lng;
}
public double getLat() {
return lat;
}
public void setLat(double lat) {
this.lat = lat;
}
public String getPhoneNum() {
return phoneNum;
}
public void setPhoneNum(String phoneNum) {
this.phoneNum = phoneNum;
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
}
public static class ReceiverInfo {
private String receiverName;
private String address;
private String phoneNum;
private double lng;
private double lat;
public String getReceiverName() {
return receiverName;
}
public void setReceiverName(String receiverName) {
this.receiverName = receiverName;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getPhoneNum() {
return phoneNum;
}
public void setPhoneNum(String phoneNum) {
this.phoneNum = phoneNum;
}
public double getLng() {
return lng;
}
public void setLng(double lng) {
this.lng = lng;
}
public double getLat() {
return lat;
}
public void setLat(double lat) {
this.lat = lat;
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
}
}

View File

@ -0,0 +1,22 @@
package cn.binarywang.wx.miniapp.bean.intractiy;
import org.apache.commons.lang3.builder.ToStringBuilder;
public class WxMaPreAddOrderRequest extends BasicWxMaOrder {
private Cargo cargo;
public Cargo getCargo() {
return cargo;
}
public void setCargo(Cargo cargo) {
this.cargo = cargo;
}
public static class Cargo extends BasicWxMaOrder.Cargo {}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
}

View File

@ -0,0 +1,88 @@
package cn.binarywang.wx.miniapp.bean.intractiy;
import java.util.Date;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class WxMaQueryFlowRequest {
private static final Logger logger = LoggerFactory.getLogger(WxMaQueryFlowRequest.class);
private String wxStoreId;
/** 流水类型: 1:充值流水, 2:消费流水3:退款流水 */
private int flowType = 1;
/** 运力ID */
private String serviceTransId;
private transient Date beginDate;
private transient Date endDate;
private long beginTime;
private long endTime;
public String getWxStoreId() {
return wxStoreId;
}
public void setWxStoreId(String wxStoreId) {
this.wxStoreId = wxStoreId;
}
public int getFlowType() {
return flowType;
}
public void setFlowType(int flowType) {
this.flowType = flowType;
}
public String getServiceTransId() {
return serviceTransId;
}
public void setServiceTransId(String serviceTransId) {
this.serviceTransId = serviceTransId;
}
public Date getBeginDate() {
return beginDate;
}
public void setBeginDate(Date beginDate) {
this.beginDate = beginDate;
this.beginTime = beginDate.getTime() / 1000;
}
public Date getEndDate() {
return endDate;
}
public void setEndDate(Date endDate) {
this.endDate = endDate;
this.endTime = endDate.getTime() / 1000;
}
public long getBeginTime() {
return beginTime;
}
public void setBeginTime(long beginTime) {
this.beginTime = beginTime;
this.beginDate = new Date(beginTime * 1000);
}
public long getEndTime() {
return endTime;
}
public void setEndTime(long endTime) {
this.endTime = endTime;
this.endDate = new Date(endTime * 1000);
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
}

View File

@ -0,0 +1,187 @@
package cn.binarywang.wx.miniapp.bean.intractiy;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class WxMaStore {
private static final Logger logger = LoggerFactory.getLogger(WxMaStore.class);
/** 微信分配的ID创建时不用填写查询时返回根据此ID下单等 */
private String wxStoreId;
/** 自己设置的门店ID创建时填写查询时返回不可修改 */
private String outStoreId;
/** 门店名称,创建时需要,可修改;查询结果微信不返回此字段 */
private String storeName;
/** 创建时不用设置,查询时返回,微信自动设置 */
private String cityId;
/** 1价格优先2运力优先 */
private int orderPattern = 1;
/**
* 运力优先时优先使用的运力运力ID请参考https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/industry/express/business/intracity_service.html#_6-%E8%BF%90%E5%8A%9B%E5%88%97%E8%A1%A8
*/
private String ServiceTransPrefer;
private AddressInfo addressInfo;
public String getWxStoreId() {
return wxStoreId;
}
public void setWxStoreId(String wxStoreId) {
this.wxStoreId = wxStoreId;
}
public String getOutStoreId() {
return outStoreId;
}
public void setOutStoreId(String outStoreId) {
this.outStoreId = outStoreId;
}
public String getStoreName() {
return storeName;
}
public void setStoreName(String storeName) {
this.storeName = storeName;
}
public String getCityId() {
return cityId;
}
public void setCityId(String cityId) {
this.cityId = cityId;
}
public int getOrderPattern() {
return orderPattern;
}
public void setOrderPattern(int orderPattern) {
this.orderPattern = orderPattern;
}
public String getServiceTransPrefer() {
return ServiceTransPrefer;
}
public void setServiceTransPrefer(String serviceTransPrefer) {
ServiceTransPrefer = serviceTransPrefer;
}
public AddressInfo getAddressInfo() {
return addressInfo;
}
public void setAddressInfo(AddressInfo addressInfo) {
this.addressInfo = addressInfo;
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
public static class AddressInfo {
/** 省、自治区、直辖市 必填 */
private String province;
/** 地级市 必填 */
private String city;
/** 区/县/县级市 必填 */
private String area;
/** 街道/镇 选填 */
private String street;
/** 路名和门牌号 必填 */
private String house;
/** 门店电话号码 必填 */
private String phone;
/** 纬度 必填 */
private double lat;
/** 经度 必填 */
private double lng;
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getArea() {
return area;
}
public void setArea(String area) {
this.area = area;
}
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
public String getHouse() {
return house;
}
public void setHouse(String house) {
this.house = house;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public double getLat() {
return lat;
}
public void setLat(double lat) {
this.lat = lat;
}
public double getLng() {
return lng;
}
public void setLng(double lng) {
this.lng = lng;
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
}
}

View File

@ -0,0 +1,115 @@
package cn.binarywang.wx.miniapp.bean.intractiy;
import java.util.Date;
import java.util.List;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class WxMaStoreBalance {
private static final Logger logger = LoggerFactory.getLogger(WxMaStoreBalance.class);
private String wxStoreId;
private String appid;
private int allBalance;
private List<Detail> balanceDetail;
public String getWxStoreId() {
return wxStoreId;
}
public void setWxStoreId(String wxStoreId) {
this.wxStoreId = wxStoreId;
}
public String getAppid() {
return appid;
}
public void setAppid(String appid) {
this.appid = appid;
}
public int getAllBalance() {
return allBalance;
}
public void setAllBalance(int allBalance) {
this.allBalance = allBalance;
}
public List<Detail> getBalanceDetail() {
return balanceDetail;
}
public void setBalanceDetail(List<Detail> balanceDetail) {
this.balanceDetail = balanceDetail;
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
public static class Detail {
private String payorderId;
private int chargeAmt;
private int unusedAmt;
private long beginTime;
private long endTime;
public String getPayorderId() {
return payorderId;
}
public void setPayorderId(String payorderId) {
this.payorderId = payorderId;
}
public int getChargeAmt() {
return chargeAmt;
}
public void setChargeAmt(int chargeAmt) {
this.chargeAmt = chargeAmt;
}
public int getUnusedAmt() {
return unusedAmt;
}
public void setUnusedAmt(int unusedAmt) {
this.unusedAmt = unusedAmt;
}
public Date getBeginDate() {
return this.beginTime == 0 ? null : new Date(this.beginTime * 1000);
}
public Date getEndDate() {
return this.endTime == 0 ? null : new Date(this.endTime * 1000);
}
public long getBeginTime() {
return beginTime;
}
public void setBeginTime(long beginTime) {
this.beginTime = beginTime;
}
public long getEndTime() {
return endTime;
}
public void setEndTime(long endTime) {
this.endTime = endTime;
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
}
}

View File

@ -0,0 +1,22 @@
package cn.binarywang.wx.miniapp.bean.intractiy;
import org.apache.commons.lang3.builder.ToStringBuilder;
public class WxMaStoreChargeRequest extends BasicWxMaStoreChargeRefundRequest {
/** 充值金额 单位:分, 50元起充 */
private int amount;
public int getAmount() {
return amount;
}
public void setAmount(int amount) {
this.amount = amount;
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
}

View File

@ -0,0 +1,318 @@
package cn.binarywang.wx.miniapp.bean.intractiy;
import java.util.Date;
import java.util.List;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class WxMaStoreFlowResponse<T extends WxMaStoreFlowResponse.BasicFlowRecord> {
private static final Logger logger = LoggerFactory.getLogger(WxMaStoreFlowResponse.class);
/** 总支付金额 */
private Long totalPayAmt;
/** 总退款金额 */
private Long totalRefundAmt;
/** 总违约金 查询消费流水才返回 */
private Long totalDeductAmt;
/** 流水 */
private List<T> flowList;
public List<T> getFlowList() {
return flowList;
}
public void setFlowList(List<T> flowList) {
this.flowList = flowList;
}
public Long getTotalPayAmt() {
return totalPayAmt;
}
public void setTotalPayAmt(Long totalPayAmt) {
this.totalPayAmt = totalPayAmt;
}
public Long getTotalRefundAmt() {
return totalRefundAmt;
}
public void setTotalRefundAmt(Long totalRefundAmt) {
this.totalRefundAmt = totalRefundAmt;
}
public Long getTotalDeductAmt() {
return totalDeductAmt;
}
public void setTotalDeductAmt(Long totalDeductAmt) {
this.totalDeductAmt = totalDeductAmt;
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
public static class BasicFlowRecord {
private int flowType;
private String appid;
private String wxStoreId;
private String payOrderId;
private String serviceTransId;
private int payAmount;
private long payTime;
private long createTime;
public int getFlowType() {
return flowType;
}
public void setFlowType(int flowType) {
this.flowType = flowType;
}
public String getAppid() {
return appid;
}
public void setAppid(String appid) {
this.appid = appid;
}
public String getWxStoreId() {
return wxStoreId;
}
public void setWxStoreId(String wxStoreId) {
this.wxStoreId = wxStoreId;
}
public String getPayOrderId() {
return payOrderId;
}
public void setPayOrderId(String payOrderId) {
this.payOrderId = payOrderId;
}
public String getServiceTransId() {
return serviceTransId;
}
public void setServiceTransId(String serviceTransId) {
this.serviceTransId = serviceTransId;
}
public int getPayAmount() {
return payAmount;
}
public void setPayAmount(int payAmount) {
this.payAmount = payAmount;
}
public Date getPayDate() {
return this.payTime == 0 ? null : new Date(this.payTime * 1000);
}
public long getPayTime() {
return payTime;
}
public void setPayTime(long payTime) {
this.payTime = payTime;
}
public Date getCreateDate() {
return this.createTime == 0 ? null : new Date(this.createTime * 1000);
}
public long getCreateTime() {
return createTime;
}
public void setCreateTime(long createTime) {
this.createTime = createTime;
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
}
/** 充值流水 */
public static class ChargeFlowRecord extends BasicFlowRecord {
private String payStatus;
private long consumeDeadline;
public String getPayStatus() {
return payStatus;
}
public void setPayStatus(String payStatus) {
this.payStatus = payStatus;
}
public Date getConsumeDeadlineDate() {
return this.consumeDeadline == 0 ? null : new Date(this.consumeDeadline * 1000);
}
public long getConsumeDeadline() {
return consumeDeadline;
}
public void setConsumeDeadline(long consumeDeadline) {
this.consumeDeadline = consumeDeadline;
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
}
/** 退款流水 */
public static class RefundFlowRecord extends BasicFlowRecord {
private int refundAmount;
private long refundTime;
private long consumeDeadline;
public int getRefundAmount() {
return refundAmount;
}
public void setRefundAmount(int refundAmount) {
this.refundAmount = refundAmount;
}
public Date getRefundDate() {
return this.refundTime == 0 ? null : new Date(this.refundTime * 1000);
}
public long getRefundTime() {
return refundTime;
}
public void setRefundTime(long refundTime) {
this.refundTime = refundTime;
}
public Date getConsumeDeadlineDate() {
return this.consumeDeadline == 0 ? null : new Date(this.consumeDeadline * 1000);
}
public long getConsumeDeadline() {
return consumeDeadline;
}
public void setConsumeDeadline(long consumeDeadline) {
this.consumeDeadline = consumeDeadline;
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
}
/** 消费流水 */
public static class ConsumeFlowRecord extends BasicFlowRecord {
private String wxOrderId;
private String openid;
private String deliveryStatus;
private String payStatus;
private String refundStatus;
private int refundAmount;
private int deductAmount;
private String billId;
private long deliveryFinishedTime;
public String getWxOrderId() {
return wxOrderId;
}
public void setWxOrderId(String wxOrderId) {
this.wxOrderId = wxOrderId;
}
public String getOpenid() {
return openid;
}
public void setOpenid(String openid) {
this.openid = openid;
}
public String getDeliveryStatus() {
return deliveryStatus;
}
public void setDeliveryStatus(String deliveryStatus) {
this.deliveryStatus = deliveryStatus;
}
public String getPayStatus() {
return payStatus;
}
public void setPayStatus(String payStatus) {
this.payStatus = payStatus;
}
public String getRefundStatus() {
return refundStatus;
}
public void setRefundStatus(String refundStatus) {
this.refundStatus = refundStatus;
}
public int getRefundAmount() {
return refundAmount;
}
public void setRefundAmount(int refundAmount) {
this.refundAmount = refundAmount;
}
public int getDeductAmount() {
return deductAmount;
}
public void setDeductAmount(int deductAmount) {
this.deductAmount = deductAmount;
}
public String getBillId() {
return billId;
}
public void setBillId(String billId) {
this.billId = billId;
}
public Date getDeliveryFinishedDate() {
return this.deliveryFinishedTime == 0 ? null : new Date(this.deliveryFinishedTime * 1000);
}
public long getDeliveryFinishedTime() {
return deliveryFinishedTime;
}
public void setDeliveryFinishedTime(long deliveryFinishedTime) {
this.deliveryFinishedTime = deliveryFinishedTime;
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
}
}

View File

@ -0,0 +1,11 @@
package cn.binarywang.wx.miniapp.bean.intractiy;
import org.apache.commons.lang3.builder.ToStringBuilder;
public class WxMaStoreRefundRequest extends BasicWxMaStoreChargeRefundRequest {
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
}

View File

@ -0,0 +1,56 @@
package cn.binarywang.wx.miniapp.bean.intractiy;
import java.util.List;
import org.apache.commons.lang3.builder.ToStringBuilder;
public class WxMaTransCity {
private String serviceTransId;
private List<City> cityList;
public String getServiceTransId() {
return serviceTransId;
}
public void setServiceTransId(String serviceTransId) {
this.serviceTransId = serviceTransId;
}
public List<City> getCityList() {
return cityList;
}
public void setCityList(List<City> cityList) {
this.cityList = cityList;
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
public static class City {
private String cityName;
private String cityCode;
public String getCityName() {
return cityName;
}
public void setCityName(String cityName) {
this.cityName = cityName;
}
public String getCityCode() {
return cityCode;
}
public void setCityCode(String cityCode) {
this.cityCode = cityCode;
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
}
}

View File

@ -1,11 +1,10 @@
package cn.binarywang.wx.miniapp.config;
import me.chanjar.weixin.common.bean.WxAccessToken;
import me.chanjar.weixin.common.bean.WxAccessTokenEntity;
import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder;
import java.util.concurrent.locks.Lock;
import java.util.function.Consumer;
import me.chanjar.weixin.common.bean.WxAccessToken;
import me.chanjar.weixin.common.bean.WxAccessTokenEntity;
import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder;
/**
* 小程序配置
@ -14,9 +13,7 @@ import java.util.function.Consumer;
*/
public interface WxMaConfig {
default void setUpdateAccessTokenBefore(Consumer<WxAccessTokenEntity> updateAccessTokenBefore) {
}
default void setUpdateAccessTokenBefore(Consumer<WxAccessTokenEntity> updateAccessTokenBefore) {}
/**
* Gets access token.
@ -25,12 +22,12 @@ public interface WxMaConfig {
*/
String getAccessToken();
//region 稳定版access token
// region 稳定版access token
boolean isStableAccessToken();
void useStableAccessToken(boolean useStableAccessToken);
//endregion
// endregion
/**
* Gets access token lock.
@ -46,9 +43,7 @@ public interface WxMaConfig {
*/
boolean isAccessTokenExpired();
/**
* 强制将access token过期掉
*/
/** 强制将access token过期掉 */
void expireAccessToken();
/**
@ -63,7 +58,7 @@ public interface WxMaConfig {
/**
* 应该是线程安全的
*
* @param accessToken 新的accessToken值
* @param accessToken 新的accessToken值
* @param expiresInSeconds 过期时间以秒为单位
*/
void updateAccessToken(String accessToken, int expiresInSeconds);
@ -77,10 +72,7 @@ public interface WxMaConfig {
updateAccessToken(accessToken, expiresInSeconds);
}
default void updateAccessTokenBefore(WxAccessTokenEntity wxAccessTokenEntity) {
}
default void updateAccessTokenBefore(WxAccessTokenEntity wxAccessTokenEntity) {}
/**
* Gets jsapi ticket.
@ -103,15 +95,13 @@ public interface WxMaConfig {
*/
boolean isJsapiTicketExpired();
/**
* 强制将jsapi ticket过期掉
*/
/** 强制将jsapi ticket过期掉 */
void expireJsapiTicket();
/**
* 应该是线程安全的
*
* @param jsapiTicket 新的jsapi ticket值
* @param jsapiTicket 新的jsapi ticket值
* @param expiresInSeconds 过期时间以秒为单位
*/
void updateJsapiTicket(String jsapiTicket, int expiresInSeconds);
@ -137,15 +127,13 @@ public interface WxMaConfig {
*/
boolean isCardApiTicketExpired();
/**
* 强制将卡券api ticket过期掉.
*/
/** 强制将卡券api ticket过期掉. */
void expireCardApiTicket();
/**
* 应该是线程安全的.
*
* @param apiTicket 新的卡券api ticket值
* @param apiTicket 新的卡券api ticket值
* @param expiresInSeconds 过期时间以秒为单位
*/
void updateCardApiTicket(String apiTicket, int expiresInSeconds);
@ -236,6 +224,7 @@ public interface WxMaConfig {
/**
* http 请求重试间隔
*
* <pre>
* {@link cn.binarywang.wx.miniapp.api.impl.BaseWxMaServiceImpl#setRetrySleepMillis(int)}
* </pre>
@ -244,6 +233,7 @@ public interface WxMaConfig {
/**
* http 请求最大重试次数
*
* <pre>
* {@link cn.binarywang.wx.miniapp.api.impl.BaseWxMaServiceImpl#setMaxRetryTimes(int)}
* </pre>
@ -288,10 +278,35 @@ public interface WxMaConfig {
String getAccessTokenUrl();
/**
* 设置自定义的获取accessToken地址
* 可用于设置获取accessToken的自定义服务
* 设置自定义的获取accessToken地址 可用于设置获取accessToken的自定义服务
*
* @param accessTokenUrl 自定义的获取accessToken地址
*/
void setAccessTokenUrl(String accessTokenUrl);
/**
* 服务端API签名用到的RSA私钥pkcs8格式会以 -----BEGIN PRIVATE KEY-----开头 'BEGIN RSA PRIVATE
* KEY'的是pkcs1格式需要转换可用openssl转换 设置参考
* https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/getting_started/api_signature.html
*
* @return rsa private key string
*/
String getApiSignatureRsaPrivateKey();
/**
* 服务端API签名用到的AES密钥
* https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/getting_started/api_signature.html
*
* @return aes key string
*/
String getApiSignatureAesKey();
/** 密钥对应的序号 */
String getApiSignatureAesKeySn();
/** 密钥对应的序号 */
String getApiSignatureRsaPrivateKeySn();
/** 密钥对应的小程序ID (普通小程序同 appId 托管第三方平台的是 componentAppId */
String getWechatMpAppid();
}

View File

@ -2,17 +2,16 @@ package cn.binarywang.wx.miniapp.config.impl;
import cn.binarywang.wx.miniapp.config.WxMaConfig;
import cn.binarywang.wx.miniapp.json.WxMaGsonBuilder;
import java.io.File;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Consumer;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
import me.chanjar.weixin.common.bean.WxAccessTokenEntity;
import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder;
import java.io.File;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Consumer;
/**
* 基于内存的微信配置provider在实际生产环境中应该将这些配置持久化
*
@ -23,30 +22,32 @@ public class WxMaDefaultConfigImpl implements WxMaConfig {
protected volatile String appid;
protected volatile String token;
/**
* 是否使用稳定版获取accessToken接口
*/
/** 是否使用稳定版获取accessToken接口 */
@Getter(value = AccessLevel.NONE)
private boolean useStableAccessToken;
/**
* 小程序原始ID
*/
/** 小程序原始ID */
protected volatile String originalId;
protected Lock accessTokenLock = new ReentrantLock();
/**
* 临时文件目录.
*/
/** 临时文件目录. */
protected volatile File tmpDirFile;
private volatile String msgDataFormat;
private volatile String secret;
private volatile String accessToken;
private volatile String aesKey;
private volatile long expiresTime;
/**
* 云环境ID
*/
private volatile String apiSignatureRsaPrivateKey;
private volatile String apiSignatureAesKey;
private volatile String apiSignatureRsaPrivateKeySn;
private volatile String apiSignatureAesKeySn;
private volatile String wechatMpAppid;
/** 云环境ID */
private volatile String cloudEnv;
private volatile String httpProxyHost;
private volatile int httpProxyPort;
private volatile String httpProxyUsername;
@ -57,10 +58,10 @@ public class WxMaDefaultConfigImpl implements WxMaConfig {
private volatile String jsapiTicket;
private volatile long jsapiTicketExpiresTime;
/**
* 微信卡券的ticket单独缓存.
*/
/** 微信卡券的ticket单独缓存. */
private volatile String cardApiTicket;
private volatile long cardApiTicketExpiresTime;
protected volatile Lock jsapiTicketLock = new ReentrantLock();
protected volatile Lock cardApiTicketLock = new ReentrantLock();
@ -68,35 +69,24 @@ public class WxMaDefaultConfigImpl implements WxMaConfig {
private String apiHostUrl;
private String accessTokenUrl;
/**
* 自定义配置token的消费者
*/
@Setter
private Consumer<WxAccessTokenEntity> updateAccessTokenBefore;
/** 自定义配置token的消费者 */
@Setter private Consumer<WxAccessTokenEntity> updateAccessTokenBefore;
/**
* 开启回调
*/
/** 开启回调 */
@Getter(AccessLevel.NONE)
private boolean enableUpdateAccessTokenBefore = true;
/**
* 可临时关闭更新token回调主要用于其他介质初始化数据时可不进行回调
*/
/** 可临时关闭更新token回调主要用于其他介质初始化数据时可不进行回调 */
public void enableUpdateAccessTokenBefore(boolean enableUpdateAccessTokenBefore) {
this.enableUpdateAccessTokenBefore = enableUpdateAccessTokenBefore;
}
/**
* 会过期的数据提前过期时间默认预留200秒的时间
*/
/** 会过期的数据提前过期时间默认预留200秒的时间 */
protected long expiresAheadInMillis(int expiresInSeconds) {
return System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L;
}
/**
* 判断 expiresTime 是否已经过期
*/
/** 判断 expiresTime 是否已经过期 */
protected boolean isExpired(long expiresTime) {
return System.currentTimeMillis() > expiresTime;
}
@ -110,7 +100,7 @@ public class WxMaDefaultConfigImpl implements WxMaConfig {
this.accessToken = accessToken;
}
//region 使用稳定版接口获取accessToken
// region 使用稳定版接口获取accessToken
@Override
public boolean isStableAccessToken() {
return this.useStableAccessToken;
@ -120,8 +110,8 @@ public class WxMaDefaultConfigImpl implements WxMaConfig {
public void useStableAccessToken(boolean useStableAccessToken) {
this.useStableAccessToken = useStableAccessToken;
}
//endregion
// endregion
@Override
public Lock getAccessTokenLock() {
@ -137,10 +127,10 @@ public class WxMaDefaultConfigImpl implements WxMaConfig {
return isExpired(this.expiresTime);
}
// @Override
// public synchronized void updateAccessToken(WxAccessToken accessToken) {
// updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn());
// }
// @Override
// public synchronized void updateAccessToken(WxAccessToken accessToken) {
// updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn());
// }
@Override
public synchronized void updateAccessToken(String accessToken, int expiresInSeconds) {
@ -248,6 +238,46 @@ public class WxMaDefaultConfigImpl implements WxMaConfig {
this.aesKey = aesKey;
}
public String getApiSignatureRsaPrivateKey() {
return apiSignatureRsaPrivateKey;
}
public void setApiSignatureRsaPrivateKey(String apiSignatureRsaPrivateKey) {
this.apiSignatureRsaPrivateKey = apiSignatureRsaPrivateKey;
}
public String getApiSignatureAesKey() {
return apiSignatureAesKey;
}
public void setApiSignatureAesKey(String apiSignatureAesKey) {
this.apiSignatureAesKey = apiSignatureAesKey;
}
public String getApiSignatureRsaPrivateKeySn() {
return apiSignatureRsaPrivateKeySn;
}
public void setApiSignatureRsaPrivateKeySn(String apiSignatureRsaPrivateKeySn) {
this.apiSignatureRsaPrivateKeySn = apiSignatureRsaPrivateKeySn;
}
public String getApiSignatureAesKeySn() {
return apiSignatureAesKeySn;
}
public void setApiSignatureAesKeySn(String apiSignatureAesKeySn) {
this.apiSignatureAesKeySn = apiSignatureAesKeySn;
}
public String getWechatMpAppid() {
return wechatMpAppid == null ? appid : wechatMpAppid;
}
public void setWechatMpAppid(String wechatMpAppid) {
this.wechatMpAppid = wechatMpAppid;
}
@Override
public String getOriginalId() {
return originalId;

View File

@ -0,0 +1,71 @@
package cn.binarywang.wx.miniapp.executor;
import cn.binarywang.wx.miniapp.bean.WxMaApiResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import me.chanjar.weixin.common.enums.WxType;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.util.http.RequestHttp;
import me.chanjar.weixin.common.util.http.apache.Utf8ResponseHandler;
import org.apache.http.Consts;
import org.apache.http.Header;
import org.apache.http.HttpHost;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ApacheApiSignaturePostRequestExecutor
extends ApiSignaturePostRequestExecutor<CloseableHttpClient, HttpHost> {
private static final Logger logger =
LoggerFactory.getLogger(ApacheApiSignaturePostRequestExecutor.class);
public ApacheApiSignaturePostRequestExecutor(RequestHttp<?, ?> requestHttp) {
super(requestHttp);
}
@Override
public WxMaApiResponse execute(
String uri, Map<String, String> headers, String postEntity, WxType wxType)
throws WxErrorException, IOException {
// logger.debug(
// "ApacheApiSignaturePostRequestExecutor.execute uri:{}, headers:{}, postData:{}",
// uri,
// headers,
// postEntity);
HttpPost httpPost = new HttpPost(uri);
if (requestHttp.getRequestHttpProxy() != null) {
RequestConfig config =
RequestConfig.custom().setProxy(requestHttp.getRequestHttpProxy()).build();
httpPost.setConfig(config);
}
if (headers != null) {
headers.forEach(httpPost::addHeader);
}
if (postEntity != null) {
StringEntity entity = new StringEntity(postEntity, Consts.UTF_8);
entity.setContentType("application/json; charset=utf-8");
httpPost.setEntity(entity);
}
try (CloseableHttpResponse response = requestHttp.getRequestHttpClient().execute(httpPost)) {
String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response);
Map<String, String> respHeaders = new HashMap<>();
Header[] rHeaders = response.getAllHeaders();
if (rHeaders != null) {
for (Header h : rHeaders) {
respHeaders.putIfAbsent(h.getName(), h.getValue());
}
}
return this.handleResponse(wxType, responseContent, respHeaders);
} finally {
httpPost.releaseConnection();
}
}
}

View File

@ -0,0 +1,69 @@
package cn.binarywang.wx.miniapp.executor;
import cn.binarywang.wx.miniapp.bean.WxMaApiResponse;
import java.io.IOException;
import java.rmi.RemoteException;
import java.util.Map;
import me.chanjar.weixin.common.enums.WxType;
import me.chanjar.weixin.common.error.WxError;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.util.http.RequestExecutor;
import me.chanjar.weixin.common.util.http.RequestHttp;
import me.chanjar.weixin.common.util.http.ResponseHandler;
import org.jetbrains.annotations.NotNull;
public abstract class ApiSignaturePostRequestExecutor<H, P>
implements RequestExecutor<WxMaApiResponse, WxMaApiResponse> {
protected RequestHttp<H, P> requestHttp;
public ApiSignaturePostRequestExecutor(RequestHttp requestHttp) {
this.requestHttp = requestHttp;
}
@Override
public WxMaApiResponse execute(String uri, WxMaApiResponse data, WxType wxType)
throws WxErrorException, IOException {
throw new RemoteException("method not implemented yet.");
}
@Override
public void execute(
String uri, WxMaApiResponse data, ResponseHandler<WxMaApiResponse> handler, WxType wxType)
throws WxErrorException, IOException {
throw new RemoteException("method not implemented yet.");
}
public abstract WxMaApiResponse execute(
String uri, Map<String, String> headers, String data, WxType wxType)
throws WxErrorException, IOException;
@NotNull
public WxMaApiResponse handleResponse(
WxType wxType, String responseContent, Map<String, String> headers) throws WxErrorException {
if (responseContent.isEmpty()) {
throw new WxErrorException("无响应内容");
}
WxError error = WxError.fromJson(responseContent, wxType);
if (error.getErrorCode() != 0) {
throw new WxErrorException(error);
}
WxMaApiResponse response = new WxMaApiResponse();
response.setContent(responseContent);
response.setHeaders(headers);
return response;
}
public static ApiSignaturePostRequestExecutor create(RequestHttp requestHttp) {
switch (requestHttp.getRequestType()) {
case APACHE_HTTP:
return new ApacheApiSignaturePostRequestExecutor(requestHttp);
case JODD_HTTP:
return new JoddApiSignaturePostRequestExecutor(requestHttp);
case OK_HTTP:
return new OkHttpApiSignaturePostRequestExecutor(requestHttp);
default:
throw new IllegalArgumentException("非法请求参数");
}
}
}

View File

@ -0,0 +1,59 @@
package cn.binarywang.wx.miniapp.executor;
import cn.binarywang.wx.miniapp.bean.WxMaApiResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import jodd.http.HttpConnectionProvider;
import jodd.http.HttpRequest;
import jodd.http.HttpResponse;
import jodd.http.ProxyInfo;
import me.chanjar.weixin.common.enums.WxType;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.util.http.RequestHttp;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class JoddApiSignaturePostRequestExecutor
extends ApiSignaturePostRequestExecutor<HttpConnectionProvider, ProxyInfo> {
private static final Logger logger =
LoggerFactory.getLogger(JoddApiSignaturePostRequestExecutor.class);
public JoddApiSignaturePostRequestExecutor(RequestHttp<?, ?> requestHttp) {
super(requestHttp);
}
@Override
public WxMaApiResponse execute(
String uri, Map<String, String> headers, String postEntity, WxType wxType)
throws WxErrorException, IOException {
// logger.debug(
// "JoddApiSignaturePostRequestExecutor.execute uri:{}, headers:{}, postData:{}",
// uri,
// headers,
// postEntity);
HttpConnectionProvider provider = requestHttp.getRequestHttpClient();
ProxyInfo proxyInfo = requestHttp.getRequestHttpProxy();
HttpRequest request = HttpRequest.post(uri);
if (proxyInfo != null) {
provider.useProxy(proxyInfo);
}
if (headers != null) {
headers.forEach(request::header);
}
request.withConnectionProvider(provider);
if (postEntity != null) {
request.contentType("application/json", "utf-8");
request.bodyText(postEntity);
}
HttpResponse response = request.send();
response.charset(StandardCharsets.UTF_8.name());
Map<String, String> respHeaders = new HashMap<>();
for (String n : response.headerNames()) {
respHeaders.putIfAbsent(n, response.header(n));
}
return this.handleResponse(wxType, response.bodyText(), respHeaders);
}
}

View File

@ -0,0 +1,51 @@
package cn.binarywang.wx.miniapp.executor;
import cn.binarywang.wx.miniapp.bean.WxMaApiResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import me.chanjar.weixin.common.enums.WxType;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.util.http.RequestHttp;
import me.chanjar.weixin.common.util.http.okhttp.OkHttpProxyInfo;
import okhttp3.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class OkHttpApiSignaturePostRequestExecutor
extends ApiSignaturePostRequestExecutor<OkHttpClient, OkHttpProxyInfo> {
private static final Logger logger =
LoggerFactory.getLogger(OkHttpApiSignaturePostRequestExecutor.class);
public OkHttpApiSignaturePostRequestExecutor(RequestHttp<?, ?> requestHttp) {
super(requestHttp);
}
@Override
public WxMaApiResponse execute(
String uri, Map<String, String> headers, String postEntity, WxType wxType)
throws WxErrorException, IOException {
// logger.debug(
// "OkHttpApiSignaturePostRequestExecutor.execute uri:{}, headers:{}, postData:{}",
// uri,
// headers,
// postEntity);
RequestBody body =
RequestBody.Companion.create(
postEntity, MediaType.parse("application/json; charset=utf-8"));
Request.Builder builder = new Request.Builder();
if (headers != null) {
headers.forEach(builder::addHeader);
}
Request request = builder.url(uri).post(body).build();
Response response = requestHttp.getRequestHttpClient().newCall(request).execute();
Map<String, String> respHeaders = new HashMap<>();
Headers rHeaders = response.headers();
for (String n : rHeaders.names()) {
respHeaders.put(n, rHeaders.get(n));
}
return this.handleResponse(
wxType, Objects.requireNonNull(response.body()).string(), respHeaders);
}
}

View File

@ -0,0 +1,234 @@
package cn.binarywang.wx.miniapp.api.impl;
import static org.testng.AssertJUnit.*;
import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.intractiy.*;
import cn.binarywang.wx.miniapp.bean.openapi.WxMiniGetApiQuotaResult;
import cn.binarywang.wx.miniapp.constant.WxMaApiUrlConstants;
import cn.binarywang.wx.miniapp.test.ApiTestModule;
import cn.binarywang.wx.miniapp.test.TestConfig;
import com.google.inject.Inject;
import java.util.ArrayList;
import java.util.List;
import me.chanjar.weixin.common.error.WxErrorException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testng.annotations.Guice;
import org.testng.annotations.Test;
@Test
@Guice(modules = ApiTestModule.class)
public class WxMaIntracityServiceImpleTest {
private static final Logger logger = LoggerFactory.getLogger(WxMaIntracityServiceImpleTest.class);
@Inject private WxMaService wxService;
@Test
public void testApiSignature() throws Exception {
WxMiniGetApiQuotaResult result =
wxService
.getWxMaOpenApiService()
.getApiQuota(
WxMaApiUrlConstants.Intracity.APPLY_URL.substring(
"https://api.weixin.qq.com".length()));
logger.info("apply 额度剩余 :{}", result.getQuota());
}
@Test
public void testApply() throws Exception {
logger.debug("testApply");
try {
wxService.getIntracityService().apply();
} catch (WxErrorException wxEx) {
if (wxEx.getError().getErrorCode() == 45009) {
// 调用分钟频率受限
} else {
throw wxEx;
}
}
}
@Test
public void testStoreRelatedApis() throws Exception {
WxMaStore store = new WxMaStore();
store.setStoreName("南京东路店");
store.setOutStoreId("njdl-001");
WxMaStore.AddressInfo addr = new WxMaStore.AddressInfo();
addr.setProvince("上海市");
addr.setCity("上海市");
addr.setArea("黄浦区");
addr.setStreet("");
addr.setHouse("南京东路690号");
addr.setLat(31.235318);
addr.setLng(121.477284);
addr.setPhone("021-23456789");
store.setAddressInfo(addr);
String wxStoreId;
List<WxMaStore> result =
wxService.getIntracityService().queryStoreByOutStoreId(store.getOutStoreId());
if (result.isEmpty()) {
wxStoreId = wxService.getIntracityService().createStore(store);
logger.debug("create store result:{}", wxStoreId);
} else {
wxStoreId = result.get(0).getWxStoreId();
}
store.setWxStoreId(wxStoreId);
addr.setPhone("021-23450000");
store.setStoreName(null);
wxService.getIntracityService().updateStore(store);
List<WxMaStore> stores = wxService.getIntracityService().listAllStores();
logger.info("listAllStores 查询到 {} 个门店 {}", stores.size(), stores);
if (stores.size() > 0) {
WxMaStore s =
wxService.getIntracityService().queryStoreByWxStoreId(stores.get(0).getWxStoreId());
assertNotNull(s);
List<WxMaStore> list =
wxService.getIntracityService().queryStoreByOutStoreId(stores.get(0).getOutStoreId());
logger.info("queryStoreByOutStoreId 查询到 {} 个门店 {}", list.size(), list);
}
}
@Test
public void testStoreChargeRelated() throws Exception {
List<WxMaStore> stores = wxService.getIntracityService().listAllStores();
if (stores.isEmpty()) {
logger.warn("没有门店,无法测试");
return;
}
WxMaStore store = stores.get(0);
WxMaGetPayModeResponse resp = wxService.getIntracityService().getPayMode();
logger.debug("查询付费主体 {}", resp);
PayMode currentPayMode = resp.getPayMode();
// 只能用当前付费模式充值否则微信接口会返回 错误代码934025, 错误信息pay_mode not match
WxMaStoreChargeRequest request = new WxMaStoreChargeRequest();
request.setPayMode(currentPayMode);
request.setWxStoreId(store.getWxStoreId());
request.setServiceTransId("DADA");
request.setAmount(5000);
String payUrl = wxService.getIntracityService().storeCharge(request);
logger.debug("充值URL:{}", payUrl);
// 查询余额
WxMaStoreBalance balance =
wxService.getIntracityService().balanceQuery(store.getWxStoreId(), null, PayMode.STORE);
logger.debug("余额 {}", balance);
// 退款
WxMaStoreRefundRequest rr = new WxMaStoreRefundRequest();
rr.setPayMode(PayMode.STORE);
rr.setWxStoreId(store.getWxStoreId());
rr.setServiceTransId("DADA");
int refundAmount = wxService.getIntracityService().storeRefund(rr);
logger.debug("退款:{}", refundAmount);
// 查询流水
WxMaQueryFlowRequest qfr = new WxMaQueryFlowRequest();
qfr.setWxStoreId(store.getWxStoreId());
WxMaStoreFlowResponse flowResponse = wxService.getIntracityService().queryFlow(qfr);
logger.debug("查询流水 {}", flowResponse);
}
@Test
public void testPayMode() throws Exception {
WxMaGetPayModeResponse resp = wxService.getIntracityService().getPayMode();
logger.debug("查询付费主体 {}", resp);
PayMode newMode = resp.getPayMode() == PayMode.APP ? PayMode.STORE : PayMode.APP;
logger.debug("set pay mode to {}", newMode);
wxService.getIntracityService().setPayMode(newMode);
WxMaGetPayModeResponse resp2 = wxService.getIntracityService().getPayMode();
logger.debug("查询付费主体 {}", resp2);
}
@Test
public void testGetCity() throws Exception {
List<WxMaTransCity> list = wxService.getIntracityService().getCity(null);
logger.debug("支持的城市 {}", list);
List<WxMaTransCity> list2 = wxService.getIntracityService().getCity("SFTC");
logger.debug("SFTC支持的城市有{}个", list2.get(0).getCityList().size());
}
@Test
public void testOrderRelatived() throws Exception {
List<WxMaStore> stores = wxService.getIntracityService().listAllStores();
if (stores.isEmpty()) {
logger.warn("没有门店,无法测试");
return;
}
String wxStoreId = stores.get(0).getWxStoreId();
{
WxMaPreAddOrderRequest request = new WxMaPreAddOrderRequest();
request.setWxStoreId(wxStoreId);
request.setUseSandbox(1);
request.setUserName("顺丰同城");
request.setUserPhone("13800000138");
request.setUserAddress("北京市海淀区学清嘉创大厦A座15层");
request.setUserLat(40.01496);
request.setUserLng(116.353093);
WxMaPreAddOrderRequest.Cargo cargo = new WxMaPreAddOrderRequest.Cargo();
cargo.setCargoName("蛋糕");
cargo.setCargoType(13);
cargo.setCargoNum(1);
cargo.setCargoPrice(10000);
cargo.setCargoWeight(1000);
request.setCargo(cargo);
WxMaAddOrderResponse response = wxService.getIntracityService().preAddOrder(request);
logger.debug("查询运费返回 {} 预估运费{}元", response, response.getFee() / 100.0);
}
String wxOrderId = null;
{
TestConfig config = (TestConfig) this.wxService.getWxMaConfig();
WxMaAddOrderRequest request = new WxMaAddOrderRequest();
request.setWxStoreId(wxStoreId);
request.setStoreOrderId("store-order-" + System.currentTimeMillis());
request.setOrderSeq("0001");
request.setUserOpenid(config.getOpenid());
request.setUseSandbox(1);
request.setUserName("顺丰同城");
request.setUserPhone("13800000138");
request.setUserAddress("北京市海淀区学清嘉创大厦A座15层");
request.setUserLat(40.01496);
request.setUserLng(116.353093);
request.setOrderDetailPath("/pages/user-center/order/detail/detail?id=xxx");
WxMaAddOrderRequest.Cargo cargo = new WxMaAddOrderRequest.Cargo();
cargo.setCargoName("蛋糕");
cargo.setCargoType(13);
cargo.setCargoNum(1);
cargo.setCargoPrice(10000);
cargo.setCargoWeight(1000);
WxMaAddOrderRequest.ItemDetail detail = new WxMaAddOrderRequest.ItemDetail();
detail.setItemName("蛋糕A");
detail.setItemPicUrl("https://www.somehost.com/aaa.jpg");
detail.setCount(1);
List<WxMaAddOrderRequest.ItemDetail> itemList = new ArrayList<>();
itemList.add(detail);
cargo.setItemList(itemList);
request.setCargo(cargo);
WxMaAddOrderResponse response = wxService.getIntracityService().addOrder(request);
wxOrderId = response.getWxOrderId();
logger.debug("创建订单返回 {} wxOrderId:{}", response, wxOrderId);
}
WxMaOrder order = wxService.getIntracityService().queryOrderByWxOrderId(wxOrderId);
logger.debug("查询订单返回 {} storeOrderId:{} ", order, order.getStoreOrderId());
WxMaOrder order2 =
wxService
.getIntracityService()
.queryOrderByStoreOrderId(wxStoreId, order.getStoreOrderId());
logger.debug("查询订单返回 {} ", order);
assertEquals(order2.getWxOrderId(), wxOrderId);
WxMaCancelOrderResponse cancelOrderResp =
wxService.getIntracityService().cancelOrderByWxOrderId(wxOrderId, 1, "不再需要");
logger.debug("取消订单返回 {} 扣费:{} ", cancelOrderResp, cancelOrderResp.getDeductfee());
try {
wxService
.getIntracityService()
.cancelOrderByStoreOrderId(wxStoreId, order.getStoreOrderId(), 1, "不再需要");
fail("重复取消未抛异常,疑似第一次取消未成功");
} catch (WxErrorException wxErrorException) {
// 订单已经被取消了重复取消会报错这里才正常
}
}
}

View File

@ -9,4 +9,9 @@
<expiresTime>可以不填写</expiresTime>
<openid>某个用户的openId</openid>
<templateId>模版消息的模版ID</templateId>
<componentApiSignatureAesKey>API签名AES密钥【没有开启API签名不要这条】</componentApiSignatureAesKey>
<componentApiSignatureAesKeySn>API签名AES密钥的序号【没有开启API签名不要这条】</componentApiSignatureAesKeySn>
<componentApiSignatureRsaPrivateKey>API签名RSA私钥的【没有开启API签名不要这条】</componentApiSignatureRsaPrivateKey>
<componentApiSignatureRsaPrivateKeySn>API签名RSA私钥的序【没有开启API签名不要这条】
</componentApiSignatureRsaPrivateKeySn>
</xml>

View File

@ -1,13 +1,12 @@
package me.chanjar.weixin.open.api;
import cn.binarywang.wx.miniapp.config.WxMaConfig;
import java.util.concurrent.locks.Lock;
import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder;
import me.chanjar.weixin.mp.config.WxMpConfigStorage;
import me.chanjar.weixin.open.bean.WxOpenAuthorizerAccessToken;
import me.chanjar.weixin.open.bean.WxOpenComponentAccessToken;
import java.util.concurrent.locks.Lock;
/**
* The interface Wx open config storage.
*
@ -99,9 +98,7 @@ public interface WxOpenConfigStorage {
*/
boolean isComponentAccessTokenExpired();
/**
* Expire component access token.
*/
/** Expire component access token. */
void expireComponentAccessToken();
/**
@ -141,6 +138,7 @@ public interface WxOpenConfigStorage {
/**
* http 请求重试间隔
*
* <pre>
* {@link me.chanjar.weixin.mp.api.impl.BaseWxMpServiceImpl#setRetrySleepMillis(int)}
* {@link cn.binarywang.wx.miniapp.api.impl.BaseWxMaServiceImpl#setRetrySleepMillis(int)}
@ -150,6 +148,7 @@ public interface WxOpenConfigStorage {
/**
* http 请求最大重试次数
*
* <pre>
* {@link me.chanjar.weixin.mp.api.impl.BaseWxMpServiceImpl#setMaxRetryTimes(int)}
* {@link cn.binarywang.wx.miniapp.api.impl.BaseWxMaServiceImpl#setMaxRetryTimes(int)}
@ -199,7 +198,7 @@ public interface WxOpenConfigStorage {
* 应该是线程安全的
*
* @param componentAccessToken 新的accessToken值
* @param expiresInSeconds 过期时间以秒为单位
* @param expiresInSeconds 过期时间以秒为单位
*/
void updateComponentAccessToken(String componentAccessToken, int expiresInSeconds);
@ -221,7 +220,7 @@ public interface WxOpenConfigStorage {
/**
* Sets authorizer refresh token.
*
* @param appId the app id
* @param appId the app id
* @param authorizerRefreshToken the authorizer refresh token
*/
void setAuthorizerRefreshToken(String appId, String authorizerRefreshToken);
@ -229,7 +228,7 @@ public interface WxOpenConfigStorage {
/**
* setAuthorizerRefreshToken(String appId, String authorizerRefreshToken) 方法重载方法
*
* @param appId the app id
* @param appId the app id
* @param authorizerRefreshToken the authorizer refresh token
*/
void updateAuthorizerRefreshToken(String appId, String authorizerRefreshToken);
@ -260,7 +259,7 @@ public interface WxOpenConfigStorage {
/**
* 应该是线程安全的
*
* @param appId the app id
* @param appId the app id
* @param authorizerAccessToken 要更新的WxAccessToken对象
*/
void updateAuthorizerAccessToken(String appId, WxOpenAuthorizerAccessToken authorizerAccessToken);
@ -268,11 +267,12 @@ public interface WxOpenConfigStorage {
/**
* 应该是线程安全的
*
* @param appId the app id
* @param appId the app id
* @param authorizerAccessToken 新的accessToken值
* @param expiresInSeconds 过期时间以秒为单位
* @param expiresInSeconds 过期时间以秒为单位
*/
void updateAuthorizerAccessToken(String appId, String authorizerAccessToken, int expiresInSeconds);
void updateAuthorizerAccessToken(
String appId, String authorizerAccessToken, int expiresInSeconds);
/**
* Gets jsapi ticket.
@ -300,8 +300,8 @@ public interface WxOpenConfigStorage {
/**
* 应该是线程安全的
*
* @param appId the app id
* @param jsapiTicket 新的jsapi ticket值
* @param appId the app id
* @param jsapiTicket 新的jsapi ticket值
* @param expiresInSeconds 过期时间以秒为单位
*/
void updateJsapiTicket(String appId, String jsapiTicket, int expiresInSeconds);
@ -314,7 +314,6 @@ public interface WxOpenConfigStorage {
*/
String getCardApiTicket(String appId);
/**
* Is card api ticket expired boolean.
*
@ -333,8 +332,8 @@ public interface WxOpenConfigStorage {
/**
* 应该是线程安全的
*
* @param appId the app id
* @param cardApiTicket 新的cardApi ticket值
* @param appId the app id
* @param cardApiTicket 新的cardApi ticket值
* @param expiresInSeconds 过期时间以秒为单位
*/
void updateCardApiTicket(String appId, String cardApiTicket, int expiresInSeconds);
@ -342,10 +341,34 @@ public interface WxOpenConfigStorage {
/**
* 设置第三方平台基础信息
*
* @param componentAppId 第三方平台 appid
* @param componentAppId 第三方平台 appid
* @param componentAppSecret 第三方平台 appsecret
* @param componentToken 消息校验Token
* @param componentAesKey 消息加解密Key
* @param componentToken 消息校验Token
* @param componentAesKey 消息加解密Key
*/
void setWxOpenInfo(String componentAppId, String componentAppSecret, String componentToken, String componentAesKey);
void setWxOpenInfo(
String componentAppId,
String componentAppSecret,
String componentToken,
String componentAesKey);
/** 第三方平台设置API签名 RSA 私钥 */
String getComponentApiSignatureRsaPrivateKey();
void setComponentApiSignatureRsaPrivateKey(String apiSignatureRsaPrivateKey);
/** 第三方平台设置API签名 AES KEY */
String getComponentApiSignatureAesKey();
void setComponentApiSignatureAesKey(String apiSignatureAesKey);
/** 第三方平台设置API签名 RSA 私钥 序号 */
String getComponentApiSignatureRsaPrivateKeySn();
void setComponentApiSignatureRsaPrivateKeySn(String apiSignatureRsaPrivateKeySn);
/** 第三方平台设置API签名 AES key 序号 */
String getComponentApiSignatureAesKeySn();
void setComponentApiSignatureAesKeySn(String apiSignatureAesKeySn);
}

View File

@ -1,7 +1,11 @@
package me.chanjar.weixin.open.api.impl;
import cn.binarywang.wx.miniapp.config.WxMaConfig;
import java.io.File;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import lombok.AccessLevel;
import lombok.Data;
import lombok.Getter;
@ -16,12 +20,6 @@ import me.chanjar.weixin.open.bean.WxOpenAuthorizerAccessToken;
import me.chanjar.weixin.open.bean.WxOpenComponentAccessToken;
import me.chanjar.weixin.open.util.json.WxOpenGsonBuilder;
import java.io.File;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 基于内存的微信配置provider在实际生产环境中应该将这些配置持久化
*
@ -37,26 +35,36 @@ public class WxOpenInMemoryConfigStorage implements WxOpenConfigStorage {
private String componentAccessToken;
private long componentExpiresTime;
private String componentApiSignatureRsaPrivateKey;
private String componentApiSignatureAesKey;
private String componentApiSignatureRsaPrivateKeySn;
private String componentApiSignatureAesKeySn;
private String httpProxyHost;
private int httpProxyPort;
private String httpProxyUsername;
private String httpProxyPassword;
/**
* http 请求重试间隔
*
* <pre>
* {@link me.chanjar.weixin.mp.api.impl.BaseWxMpServiceImpl#setRetrySleepMillis(int)}
* {@link cn.binarywang.wx.miniapp.api.impl.BaseWxMaServiceImpl#setRetrySleepMillis(int)}
* </pre>
*/
private int retrySleepMillis = 1000;
/**
* http 请求最大重试次数
*
* <pre>
* {@link me.chanjar.weixin.mp.api.impl.BaseWxMpServiceImpl#setMaxRetryTimes(int)}
* {@link cn.binarywang.wx.miniapp.api.impl.BaseWxMaServiceImpl#setMaxRetryTimes(int)}
* </pre>
*/
private int maxRetryTimes = 5;
private ApacheHttpClientBuilder apacheHttpClientBuilder;
private Map<String, Token> authorizerRefreshTokens = new ConcurrentHashMap<>();
@ -77,7 +85,8 @@ public class WxOpenInMemoryConfigStorage implements WxOpenConfigStorage {
@Override
public void updateComponentAccessToken(WxOpenComponentAccessToken componentAccessToken) {
updateComponentAccessToken(componentAccessToken.getComponentAccessToken(), componentAccessToken.getExpiresIn());
updateComponentAccessToken(
componentAccessToken.getComponentAccessToken(), componentAccessToken.getExpiresIn());
}
private Lock accessTokenLockInstance;
@ -126,8 +135,11 @@ public class WxOpenInMemoryConfigStorage implements WxOpenConfigStorage {
}
@Override
public void setWxOpenInfo(String componentAppId, String componentAppSecret, String componentToken,
String componentAesKey) {
public void setWxOpenInfo(
String componentAppId,
String componentAppSecret,
String componentToken,
String componentAesKey) {
setComponentAppId(componentAppId);
setComponentAppSecret(componentAppSecret);
setComponentToken(componentToken);
@ -141,7 +153,8 @@ public class WxOpenInMemoryConfigStorage implements WxOpenConfigStorage {
private String getTokenString(Map<String, Token> map, String key) {
Token token = map.get(key);
if (token == null || (token.expiresTime != null && System.currentTimeMillis() > token.expiresTime)) {
if (token == null
|| (token.expiresTime != null && System.currentTimeMillis() > token.expiresTime)) {
return null;
}
return token.token;
@ -154,7 +167,8 @@ public class WxOpenInMemoryConfigStorage implements WxOpenConfigStorage {
}
}
private void updateToken(Map<String, Token> map, String key, String tokenString, Integer expiresInSeconds) {
private void updateToken(
Map<String, Token> map, String key, String tokenString, Integer expiresInSeconds) {
Token token = map.get(key);
if (token == null) {
token = new Token();
@ -186,7 +200,6 @@ public class WxOpenInMemoryConfigStorage implements WxOpenConfigStorage {
return getTokenString(authorizerAccessTokens, appId);
}
@Override
public boolean isAuthorizerAccessTokenExpired(String appId) {
return getTokenString(authorizerAccessTokens, appId) == null;
@ -198,13 +211,17 @@ public class WxOpenInMemoryConfigStorage implements WxOpenConfigStorage {
}
@Override
public void updateAuthorizerAccessToken(String appId, WxOpenAuthorizerAccessToken authorizerAccessToken) {
updateAuthorizerAccessToken(appId, authorizerAccessToken.getAuthorizerAccessToken(),
authorizerAccessToken.getExpiresIn());
public void updateAuthorizerAccessToken(
String appId, WxOpenAuthorizerAccessToken authorizerAccessToken) {
updateAuthorizerAccessToken(
appId,
authorizerAccessToken.getAuthorizerAccessToken(),
authorizerAccessToken.getExpiresIn());
}
@Override
public void updateAuthorizerAccessToken(String appId, String authorizerAccessToken, int expiresInSeconds) {
public void updateAuthorizerAccessToken(
String appId, String authorizerAccessToken, int expiresInSeconds) {
updateToken(authorizerAccessTokens, appId, authorizerAccessToken, expiresInSeconds);
}
@ -261,21 +278,18 @@ public class WxOpenInMemoryConfigStorage implements WxOpenConfigStorage {
private WxMpHostConfig hostConfig;
private String apiHostUrl;
private String accessTokenUrl;
/**
* 是否使用稳定版获取accessToken接口
*/
/** 是否使用稳定版获取accessToken接口 */
@Getter(value = AccessLevel.NONE)
@Setter(value = AccessLevel.NONE)
private boolean useStableAccessToken;
/**
* 小程序原始ID
*/
/** 小程序原始ID */
private volatile String originalId;
/**
* 云环境ID
*/
/** 云环境ID */
private volatile String cloudEnv;
private final Lock accessTokenLock;
private final Lock jsapiTicketLock;
private final Lock cardApiTicketLock;
@ -326,15 +340,18 @@ public class WxOpenInMemoryConfigStorage implements WxOpenConfigStorage {
@Override
public String getTicket(TicketType type) {
switch (type) {
case JSAPI: {
return wxOpenConfigStorage.getJsapiTicket(appId);
}
case WX_CARD: {
return wxOpenConfigStorage.getCardApiTicket(appId);
}
default: {
// do nothing
}
case JSAPI:
{
return wxOpenConfigStorage.getJsapiTicket(appId);
}
case WX_CARD:
{
return wxOpenConfigStorage.getCardApiTicket(appId);
}
default:
{
// do nothing
}
}
return null;
}
@ -342,15 +359,18 @@ public class WxOpenInMemoryConfigStorage implements WxOpenConfigStorage {
@Override
public Lock getTicketLock(TicketType type) {
switch (type) {
case JSAPI: {
return this.jsapiTicketLock;
}
case WX_CARD: {
return this.cardApiTicketLock;
}
default: {
// do nothing
}
case JSAPI:
{
return this.jsapiTicketLock;
}
case WX_CARD:
{
return this.cardApiTicketLock;
}
default:
{
// do nothing
}
}
return null;
}
@ -358,15 +378,18 @@ public class WxOpenInMemoryConfigStorage implements WxOpenConfigStorage {
@Override
public boolean isTicketExpired(TicketType type) {
switch (type) {
case JSAPI: {
return wxOpenConfigStorage.isJsapiTicketExpired(appId);
}
case WX_CARD: {
return wxOpenConfigStorage.isCardApiTicketExpired(appId);
}
default: {
// do nothing
}
case JSAPI:
{
return wxOpenConfigStorage.isJsapiTicketExpired(appId);
}
case WX_CARD:
{
return wxOpenConfigStorage.isCardApiTicketExpired(appId);
}
default:
{
// do nothing
}
}
return false;
@ -375,36 +398,41 @@ public class WxOpenInMemoryConfigStorage implements WxOpenConfigStorage {
@Override
public void expireTicket(TicketType type) {
switch (type) {
case JSAPI: {
wxOpenConfigStorage.expireJsapiTicket(appId);
break;
}
case WX_CARD: {
wxOpenConfigStorage.expireCardApiTicket(appId);
break;
}
default: {
// do nothing
}
case JSAPI:
{
wxOpenConfigStorage.expireJsapiTicket(appId);
break;
}
case WX_CARD:
{
wxOpenConfigStorage.expireCardApiTicket(appId);
break;
}
default:
{
// do nothing
}
}
}
@Override
public void updateTicket(TicketType type, String ticket, int expiresInSeconds) {
switch (type) {
case JSAPI: {
wxOpenConfigStorage.updateJsapiTicket(appId, ticket, expiresInSeconds);
break;
}
case WX_CARD: {
wxOpenConfigStorage.updateCardApiTicket(appId, ticket, expiresInSeconds);
break;
}
default: {
// do nothing
}
case JSAPI:
{
wxOpenConfigStorage.updateJsapiTicket(appId, ticket, expiresInSeconds);
break;
}
case WX_CARD:
{
wxOpenConfigStorage.updateCardApiTicket(appId, ticket, expiresInSeconds);
break;
}
default:
{
// do nothing
}
}
}
@Override
@ -510,12 +538,35 @@ public class WxOpenInMemoryConfigStorage implements WxOpenConfigStorage {
return 0;
}
@Override
public String getAesKey() {
return wxOpenConfigStorage.getComponentAesKey();
}
@Override
public String getApiSignatureRsaPrivateKey() {
return wxOpenConfigStorage.getComponentApiSignatureRsaPrivateKey();
}
@Override
public String getApiSignatureAesKey() {
return wxOpenConfigStorage.getComponentApiSignatureAesKey();
}
public String getApiSignatureRsaPrivateKeySn() {
return wxOpenConfigStorage.getComponentApiSignatureRsaPrivateKeySn();
}
@Override
public String getApiSignatureAesKeySn() {
return wxOpenConfigStorage.getComponentApiSignatureAesKeySn();
}
@Override
public String getWechatMpAppid() {
return wxOpenConfigStorage.getComponentAppId();
}
@Override
public String getMsgDataFormat() {
return null;