diff --git a/pom.xml b/pom.xml index 61a047391..7f6b66a64 100644 --- a/pom.xml +++ b/pom.xml @@ -44,6 +44,7 @@ 4.5 1.7.10 1.1.2 + 3.6.7 @@ -68,6 +69,11 @@ httpmime ${httpclient.version} + + org.jodd + jodd-http + ${jodd-http.version} + com.google.code.gson gson diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxConsts.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxConsts.java index 0455e6c2b..85efcd3fa 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxConsts.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxConsts.java @@ -47,7 +47,7 @@ public class WxConsts { /////////////////////// public static final String MASS_ST_SUCCESS = "send success"; public static final String MASS_ST_FAIL = "send fail"; - public static final String MASS_ST_涉嫌广告 = "err(10001)"; + public static final String MASS_ST_涉嫌广告 = "err(10001)"; public static final String MASS_ST_涉嫌政治 = "err(20001)"; public static final String MASS_ST_涉嫌社会 = "err(20004)"; public static final String MASS_ST_涉嫌色情 = "err(20002)"; @@ -93,6 +93,15 @@ public class WxConsts { public static final String EVT_LOCATION_SELECT = "location_select"; public static final String EVT_TEMPLATESENDJOBFINISH = "TEMPLATESENDJOBFINISH"; public static final String EVT_ENTER_AGENT = "enter_agent"; + public static final String EVT_CARD_PASS_CHECK = "card_pass_check"; + public static final String EVT_CARD_NOT_PASS_CHECK = "card_not_pass_check"; + public static final String EVT_USER_GET_CARD = "user_get_card"; + public static final String EVT_USER_DEL_CARD = "user_del_card"; + public static final String EVT_USER_CONSUME_CARD = "user_consume_card"; + public static final String EVT_USER_PAY_FROM_PAY_CELL = "user_pay_from_pay_cell"; + public static final String EVT_USER_VIEW_CARD = "user_view_card"; + public static final String EVT_USER_ENTER_SESSION_FROM_CARD = "user_enter_session_from_card"; + public static final String EVT_CARD_SKU_REMIND = "card_sku_remind"; // 库存报警 /////////////////////// // 上传多媒体文件的类型 diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/WxCardApiSignature.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/WxCardApiSignature.java new file mode 100644 index 000000000..5d4d46a9c --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/WxCardApiSignature.java @@ -0,0 +1,102 @@ +package me.chanjar.weixin.common.bean; + +import java.io.Serializable; + +/** + * 卡券Api签名 + * + * @author YuJian + * @version 15/11/8 + */ +public class WxCardApiSignature implements Serializable { + + private String appId; + + private String cardId; + + private String cardType; + + private String locationId; + + private String code; + + private String openId; + + private Long timestamp; + + private String nonceStr; + + private String signature; + + public String getAppId() { + return appId; + } + + public void setAppId(String appId) { + this.appId = appId; + } + + public String getCardId() { + return cardId; + } + + public void setCardId(String cardId) { + this.cardId = cardId; + } + + public String getCardType() { + return cardType; + } + + public void setCardType(String cardType) { + this.cardType = cardType; + } + + public String getLocationId() { + return locationId; + } + + public void setLocationId(String locationId) { + this.locationId = locationId; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getOpenId() { + return openId; + } + + public void setOpenId(String openId) { + this.openId = openId; + } + + public Long getTimestamp() { + return timestamp; + } + + public void setTimestamp(Long timestamp) { + this.timestamp = timestamp; + } + + public String getNonceStr() { + return nonceStr; + } + + public void setNonceStr(String nonceStr) { + this.nonceStr = nonceStr; + } + + public String getSignature() { + return signature; + } + + public void setSignature(String signature) { + this.signature = signature; + } +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/WxMenu.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/WxMenu.java index e20be680f..ccc7bc490 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/WxMenu.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/WxMenu.java @@ -1,15 +1,14 @@ package me.chanjar.weixin.common.bean; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; +import org.apache.commons.codec.Charsets; + import java.io.InputStream; import java.io.InputStreamReader; import java.io.Serializable; import java.util.ArrayList; import java.util.List; -import org.apache.commons.codec.Charsets; - -import me.chanjar.weixin.common.util.json.WxGsonBuilder; - /** * 企业号菜单 * @author Daniel Qian @@ -136,6 +135,7 @@ public class WxMenu implements Serializable { private String province; private String city; private String clientPlatformType; + private String language; public String getGroupId() { return groupId; @@ -184,8 +184,16 @@ public class WxMenu implements Serializable { public void setClientPlatformType(String clientPlatformType) { this.clientPlatformType = clientPlatformType; } - - @Override + + public String getLanguage() { + return language; + } + + public void setLanguage(String language) { + this.language = language; + } + + @Override public String toString() { return "matchrule:{" + "group_id='" + groupId + '\'' + @@ -194,6 +202,7 @@ public class WxMenu implements Serializable { ", province" + province + '\'' + ", city" + city + '\'' + ", client_platform_type" + clientPlatformType + '\'' + + ", language" + language + '\'' + "}"; } } diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/JoddGetRequestExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/JoddGetRequestExecutor.java new file mode 100644 index 000000000..82e28f7fa --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/JoddGetRequestExecutor.java @@ -0,0 +1,56 @@ +package me.chanjar.weixin.common.util.http; + +import jodd.http.HttpRequest; +import jodd.http.HttpResponse; +import jodd.http.ProxyInfo; +import jodd.http.net.SocketHttpConnectionProvider; +import me.chanjar.weixin.common.bean.result.WxError; +import me.chanjar.weixin.common.exception.WxErrorException; +import org.apache.http.HttpHost; +import org.apache.http.impl.client.CloseableHttpClient; + +import java.io.IOException; + +/** + * 简单的GET请求执行器,请求的参数是String, 返回的结果也是String + * + * @author Daniel Qian + */ +public class JoddGetRequestExecutor implements RequestExecutor { + + @Override + public String execute(CloseableHttpClient httpclient, HttpHost httpProxy, String uri, + String queryParam) throws WxErrorException, IOException { + if (queryParam != null) { + if (uri.indexOf('?') == -1) { + uri += '?'; + } + uri += uri.endsWith("?") ? queryParam : '&' + queryParam; + } + + SocketHttpConnectionProvider provider = new SocketHttpConnectionProvider(); + + if (httpProxy != null) { + ProxyInfo proxyInfoObj = new ProxyInfo( + ProxyInfo.ProxyType.HTTP, + httpProxy.getAddress().getHostAddress(), + httpProxy.getPort(), "", ""); + provider.useProxy(proxyInfoObj); + } + + HttpRequest request = HttpRequest.get(uri); + request.method("GET"); + request.charset("UTF-8"); + + HttpResponse response = request.open(provider).send(); + response.charset("UTF-8"); + String result = response.bodyText(); + + WxError error = WxError.fromJson(result); + if (error.getErrorCode() != 0) { + throw new WxErrorException(error); + } + return result; + } + +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/JoddPostRequestExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/JoddPostRequestExecutor.java new file mode 100644 index 000000000..d054835b4 --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/JoddPostRequestExecutor.java @@ -0,0 +1,50 @@ +package me.chanjar.weixin.common.util.http; + +import jodd.http.HttpRequest; +import jodd.http.HttpResponse; +import jodd.http.ProxyInfo; +import jodd.http.net.SocketHttpConnectionProvider; +import me.chanjar.weixin.common.bean.result.WxError; +import me.chanjar.weixin.common.exception.WxErrorException; +import org.apache.http.HttpHost; +import org.apache.http.impl.client.CloseableHttpClient; + +import java.io.IOException; + +/** + * 简单的POST请求执行器,请求的参数是String, 返回的结果也是String + * + * @author Edison Guo + */ +public class JoddPostRequestExecutor implements RequestExecutor { + + @Override + public String execute(CloseableHttpClient httpclient, HttpHost httpProxy, String uri, + String postEntity) throws WxErrorException, IOException { + SocketHttpConnectionProvider provider = new SocketHttpConnectionProvider(); + + if (httpProxy != null) { + ProxyInfo proxyInfoObj = new ProxyInfo( + ProxyInfo.ProxyType.HTTP, + httpProxy.getAddress().getHostAddress(), + httpProxy.getPort(), "", ""); + provider.useProxy(proxyInfoObj); + } + + HttpRequest request = HttpRequest.get(uri); + request.method("POST"); + request.charset("UTF-8"); + request.bodyText(postEntity); + + HttpResponse response = request.open(provider).send(); + response.charset("UTF-8"); + String result = response.bodyText(); + + WxError error = WxError.fromJson(result); + if (error.getErrorCode() != 0) { + throw new WxErrorException(error); + } + return result; + } + +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/WxMenuGsonAdapter.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/WxMenuGsonAdapter.java index cb3e8c69f..d4d9ea5ae 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/WxMenuGsonAdapter.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/WxMenuGsonAdapter.java @@ -8,9 +8,6 @@ */ package me.chanjar.weixin.common.util.json; -import java.lang.reflect.Type; - -import com.google.gson.Gson; import com.google.gson.JsonArray; import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializer; @@ -19,9 +16,10 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParseException; import com.google.gson.JsonSerializationContext; import com.google.gson.JsonSerializer; - import me.chanjar.weixin.common.bean.WxMenu; +import java.lang.reflect.Type; + /** * * @author Daniel Qian @@ -40,8 +38,7 @@ public class WxMenuGsonAdapter implements JsonSerializer, JsonDeserializ json.add("button", buttonArray); if (menu.getMatchRule() != null) { - Gson gson = new Gson(); - json.add("matchrule", gson.toJsonTree(menu.getMatchRule())); + json.add("matchrule", convertToJson(menu.getMatchRule())); } return json; @@ -63,6 +60,18 @@ public class WxMenuGsonAdapter implements JsonSerializer, JsonDeserializ return buttonJson; } + protected JsonObject convertToJson(WxMenu.WxMenuRule menuRule){ + JsonObject matchRule = new JsonObject(); + matchRule.addProperty("group_id",menuRule.getGroupId()); + matchRule.addProperty("sex",menuRule.getSex()); + matchRule.addProperty("country",menuRule.getCountry()); + matchRule.addProperty("province",menuRule.getProvince()); + matchRule.addProperty("city",menuRule.getCity()); + matchRule.addProperty("client_platform_type",menuRule.getClientPlatformType()); + matchRule.addProperty("language",menuRule.getLanguage()); + return matchRule; + } + public WxMenu deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { /* * 操蛋的微信 diff --git a/weixin-java-common/src/test/java/me/chanjar/weixin/common/bean/WxMenuTest.java b/weixin-java-common/src/test/java/me/chanjar/weixin/common/bean/WxMenuTest.java index 015889d7b..79d28e477 100644 --- a/weixin-java-common/src/test/java/me/chanjar/weixin/common/bean/WxMenuTest.java +++ b/weixin-java-common/src/test/java/me/chanjar/weixin/common/bean/WxMenuTest.java @@ -55,6 +55,29 @@ public class WxMenuTest { Assert.assertEquals(menu.toJson(), json); } + + @Test(dataProvider = "wxAddConditionalMenu") + public void testAddConditionalToJson(String json) { + WxMenu menu = new WxMenu(); + WxMenuButton button1 = new WxMenuButton(); + button1.setType("click"); + button1.setName("今日歌曲"); + button1.setKey("V1001_TODAY_MUSIC"); + + menu.getButtons().add(button1); + + WxMenu.WxMenuRule wxMenuRule = new WxMenu.WxMenuRule(); + wxMenuRule.setGroupId("2"); + wxMenuRule.setSex("1"); + wxMenuRule.setCountry("中国"); + wxMenuRule.setProvince("广东"); + wxMenuRule.setCity("广州"); + wxMenuRule.setClientPlatformType("2"); + wxMenuRule.setLanguage("zh_CN"); + menu.setMatchRule(wxMenuRule); + + Assert.assertEquals(menu.toJson(), json); + } @DataProvider public Object[][] wxReturnMenu() { @@ -106,5 +129,31 @@ public class WxMenuTest { new Object[] { json } }; } + + @DataProvider(name = "wxAddConditionalMenu") + public Object[][] addConditionalMenuJson(){ + String json = + "{" + +"\"button\":[" + +"{" + +"\"type\":\"click\"," + +"\"name\":\"今日歌曲\"," + +"\"key\":\"V1001_TODAY_MUSIC\"" + +"}" + +"]," + +"\"matchrule\":{" + +"\"group_id\":\"2\"," + +"\"sex\":\"1\"," + +"\"country\":\"中国\"," + +"\"province\":\"广东\"," + +"\"city\":\"广州\"," + +"\"client_platform_type\":\"2\"," + +"\"language\":\"zh_CN\"" + +"}" + +"}"; + return new Object[][]{ + new Object[]{json} + }; + } } diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpServiceImpl.java index 3b9e8fe77..a34108b34 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpServiceImpl.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpServiceImpl.java @@ -25,8 +25,8 @@ import me.chanjar.weixin.common.util.fs.FileUtils; import me.chanjar.weixin.common.util.http.MediaDownloadRequestExecutor; import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor; import me.chanjar.weixin.common.util.http.RequestExecutor; -import me.chanjar.weixin.common.util.http.SimpleGetRequestExecutor; -import me.chanjar.weixin.common.util.http.SimplePostRequestExecutor; +import me.chanjar.weixin.common.util.http.JoddGetRequestExecutor; +import me.chanjar.weixin.common.util.http.JoddPostRequestExecutor; import me.chanjar.weixin.common.util.http.URIUtil; import me.chanjar.weixin.common.util.json.GsonHelper; import me.chanjar.weixin.cp.bean.WxCpDepart; @@ -156,7 +156,7 @@ public class WxCpServiceImpl implements WxCpService { synchronized (globalJsapiTicketRefreshLock) { if (wxCpConfigStorage.isJsapiTicketExpired()) { String url = "https://qyapi.weixin.qq.com/cgi-bin/get_jsapi_ticket"; - String responseContent = execute(new SimpleGetRequestExecutor(), url, null); + String responseContent = execute(new JoddGetRequestExecutor(), url, null); JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent))); JsonObject tmpJsonObject = tmpJsonElement.getAsJsonObject(); String jsapiTicket = tmpJsonObject.get("ticket").getAsString(); @@ -256,7 +256,7 @@ public class WxCpServiceImpl implements WxCpService { public Integer departCreate(WxCpDepart depart) throws WxErrorException { String url = "https://qyapi.weixin.qq.com/cgi-bin/department/create"; String responseContent = execute( - new SimplePostRequestExecutor(), + new JoddPostRequestExecutor(), url, depart.toJson()); JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent))); @@ -512,11 +512,11 @@ public class WxCpServiceImpl implements WxCpService { } public String get(String url, String queryParam) throws WxErrorException { - return execute(new SimpleGetRequestExecutor(), url, queryParam); + return execute(new JoddGetRequestExecutor(), url, queryParam); } public String post(String url, String postData) throws WxErrorException { - return execute(new SimplePostRequestExecutor(), url, postData); + return execute(new JoddPostRequestExecutor(), url, postData); } /** @@ -685,12 +685,5 @@ public class WxCpServiceImpl implements WxCpService { this.tmpDirFile = tmpDirFile; } - public static void main(String[] args) { - Float a = 3.1f; - System.out.println(3.1d); - System.out.println(new BigDecimal(3.1d)); - System.out.println(new BigDecimal(a)); - System.out.println(a.toString()); - System.out.println(a.doubleValue()); - } + } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpConfigStorage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpConfigStorage.java index 09d90d649..14cc03bf7 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpConfigStorage.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpConfigStorage.java @@ -50,6 +50,21 @@ public interface WxMpConfigStorage { */ public void updateJsapiTicket(String jsapiTicket, int expiresInSeconds); + public String getCardApiTicket(); + + public boolean isCardApiTicketExpired(); + + /** + * 强制将卡券api ticket过期掉 + */ + public void expireCardApiTicket(); + + /** + * 应该是线程安全的 + * @param cardApiTicket + */ + public void updateCardApiTicket(String cardApiTicket, int expiresInSeconds); + public String getAppId(); public String getSecret(); diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpInMemoryConfigStorage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpInMemoryConfigStorage.java index e0c7aaa3b..9eacbe01b 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpInMemoryConfigStorage.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpInMemoryConfigStorage.java @@ -32,6 +32,9 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage { protected volatile String jsapiTicket; protected volatile long jsapiTicketExpiresTime; + protected volatile String cardApiTicket; + protected volatile long cardApiTicketExpiresTime; + /** * 临时文件目录 */ @@ -90,6 +93,27 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage { this.jsapiTicketExpiresTime = 0; } + /** + * 卡券api_ticket + */ + public String getCardApiTicket() { + return cardApiTicket; + } + + public boolean isCardApiTicketExpired() { + return System.currentTimeMillis() > this.cardApiTicketExpiresTime; + } + + public synchronized void updateCardApiTicket(String cardApiTicket, int expiresInSeconds) { + this.cardApiTicket = cardApiTicket; + // 预留200秒的时间 + this.cardApiTicketExpiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000l; + } + + public void expireCardApiTicket() { + this.cardApiTicketExpiresTime = 0; + } + public String getAppId() { return this.appId; } @@ -192,6 +216,8 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage { ", http_proxy_password='" + http_proxy_password + '\'' + ", jsapiTicket='" + jsapiTicket + '\'' + ", jsapiTicketExpiresTime='" + jsapiTicketExpiresTime + '\'' + + ", cardApiTicket='" + cardApiTicket + '\'' + + ", cardApiTicketExpiresTime='" + cardApiTicketExpiresTime + '\'' + ", tmpDirFile='" + tmpDirFile + '\'' + '}'; } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java index e6ce30cff..af574c1bc 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java @@ -1,5 +1,6 @@ package me.chanjar.weixin.mp.api; +import me.chanjar.weixin.common.bean.WxCardApiSignature; import me.chanjar.weixin.common.bean.WxMenu; import me.chanjar.weixin.common.bean.WxJsapiSignature; import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; @@ -725,8 +726,9 @@ public interface WxMpService { * @param parameters * the required or optional parameters * @return + * @throws WxErrorException */ - Map getJSSDKPayInfo(Map parameters); + Map getJSSDKPayInfo(Map parameters) throws WxErrorException; /** * 该接口调用“统一下单”接口,并拼装JSSDK发起支付请求需要的参数 @@ -739,10 +741,11 @@ public interface WxMpService { * @param ip 发起支付的客户端IP * @param notifyUrl 通知地址 * @return + * @throws WxErrorException * @deprecated Use me.chanjar.weixin.mp.api.WxMpService.getJSSDKPayInfo(Map) instead */ @Deprecated - Map getJSSDKPayInfo(String openId, String outTradeNo, double amt, String body, String tradeType, String ip, String notifyUrl); + Map getJSSDKPayInfo(String openId, String outTradeNo, double amt, String body, String tradeType, String ip, String notifyUrl) throws WxErrorException; /** * 该接口提供所有微信支付订单的查询,当支付通知处理异常戒丢失的情冴,商户可以通过该接口查询订单支付状态。 @@ -760,6 +763,20 @@ public interface WxMpService { */ WxMpPayCallback getJSSDKCallbackData(String xmlData); + /** + * 微信支付-申请退款 + * 详见 https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_4 + * @param parameters 需要传入的退款参数的Map。以下几项为参数的必须项:
+ *
  • transaction_id + *
  • out_trade_no (仅在上述transaction_id为空时是必须项) + *
  • out_refund_no + *
  • total_fee + *
  • refund_fee + * @return 退款操作结果 + * @throws WxErrorException + */ + public WxMpPayRefundResult refundPay(Map parameters) throws WxErrorException; + /** *
          * 计算Map键值对是否和签名相符,
    @@ -770,7 +787,7 @@ public interface WxMpService {
          * @return
          */
         public boolean checkJSSDKCallbackDataSignature(Map kvm, String signature);
    -    
    +
        /**
         * 发送微信红包给个人用户
         * @param parameters
    @@ -778,4 +795,95 @@ public interface WxMpService {
         * @throws WxErrorException
         */
         public WxRedpackResult sendRedpack(Map parameters) throws WxErrorException;
    +
    +  /**
    +   * 获得卡券api_ticket,不强制刷新卡券api_ticket
    +   * @see #getCardApiTicket(boolean)
    +   * @return 卡券api_ticket
    +   * @throws WxErrorException
    +   */
    +  public String getCardApiTicket() throws WxErrorException;
    +
    +  /**
    +   * 
    +   * 获得卡券api_ticket
    +   * 获得时会检查卡券apiToken是否过期,如果过期了,那么就刷新一下,否则就什么都不干
    +   *
    +   * 详情请见:http://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html#.E9.99.84.E5.BD.954-.E5.8D.A1.E5.88.B8.E6.89.A9.E5.B1.95.E5.AD.97.E6.AE.B5.E5.8F.8A.E7.AD.BE.E5.90.8D.E7.94.9F.E6.88.90.E7.AE.97.E6.B3.95
    +   * 
    + * @param forceRefresh 强制刷新 + * @return 卡券api_ticket + * @throws WxErrorException + */ + public String getCardApiTicket(boolean forceRefresh) throws WxErrorException; + + /** + *
    +   * 创建调用卡券api时所需要的签名
    +   *
    +   * 详情请见:http://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html#.E9.99.84.E5.BD
    +   * .954-.E5.8D.A1.E5.88.B8.E6.89.A9.E5.B1.95.E5.AD.97.E6.AE.B5.E5.8F.8A.E7.AD.BE.E5.90.8D.E7.94
    +   * .9F.E6.88.90.E7.AE.97.E6.B3.95
    +   * 
    + * + * @param optionalSignParam 参与签名的参数数组。 + * 可以为下列字段:app_id, card_id, card_type, code, openid, location_id + *
    注意:当做wx.chooseCard调用时,必须传入app_id参与签名,否则会造成签名失败导致拉取卡券列表为空 + * @return 卡券Api签名对象 + */ + public WxCardApiSignature createCardApiSignature(String... optionalSignParam) throws + WxErrorException; + + /** + * 卡券Code解码 + * @param encryptCode 加密Code,通过JSSDK的chooseCard接口获得 + * @return 解密后的Code + * @throws WxErrorException + */ + public String decryptCardCode(String encryptCode) throws WxErrorException; + + /** + * 卡券Code查询 + * @param cardId 卡券ID代表一类卡券 + * @param code 单张卡券的唯一标准 + * @param checkConsume 是否校验code核销状态,填入true和false时的code异常状态返回数据不同 + * @return WxMpCardResult对象 + * @throws WxErrorException + */ + public WxMpCardResult queryCardCode(String cardId, String code, boolean checkConsume) + throws WxErrorException; + + /** + * 卡券Code核销。核销失败会抛出异常 + * @param code 单张卡券的唯一标准 + * @param cardId 当自定义Code卡券时需要传入card_id + * @return 调用返回的JSON字符串。 + *
    可用 com.google.gson.JsonParser#parse 等方法直接取JSON串中的errcode等信息。 + * @throws WxErrorException + */ + public String consumeCardCode(String code, String cardId) throws WxErrorException; + + /** + * 卡券Mark接口。 + * 开发者在帮助消费者核销卡券之前,必须帮助先将此code(卡券串码)与一个openid绑定(即mark住), + * 才能进一步调用核销接口,否则报错。 + * @param code 卡券的code码 + * @param cardId 卡券的ID + * @param openId 用券用户的openid + * @param isMark 是否要mark(占用)这个code,填写true或者false,表示占用或解除占用 + * @throws WxErrorException + */ + public void markCardCode(String code, String cardId, String openId, boolean isMark) throws + WxErrorException; + + /** + * 查看卡券详情接口 + * 详见 https://mp.weixin.qq.com/wiki/14/8dd77aeaee85f922db5f8aa6386d385e.html#.E6.9F.A5.E7.9C.8B.E5.8D.A1.E5.88.B8.E8.AF.A6.E6.83.85 + * @param cardId 卡券的ID + * @return 返回的卡券详情JSON字符串 + *
    [注] 由于返回的JSON格式过于复杂,难以定义其对应格式的Bean并且难以维护,因此只返回String格式的JSON串。 + *
    可由 com.google.gson.JsonParser#parse 等方法直接取JSON串中的某个字段。 + * @throws WxErrorException + */ + public String getCardDetail(String cardId) throws WxErrorException; } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java index 4223caef3..99bb84637 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java @@ -5,6 +5,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.StringReader; import java.security.NoSuchAlgorithmException; +import java.util.Arrays; import java.util.Date; import java.util.HashMap; import java.util.List; @@ -14,8 +15,10 @@ import java.util.SortedMap; import java.util.TreeMap; import java.util.UUID; +import com.google.gson.JsonPrimitive; import me.chanjar.weixin.common.api.WxConsts; import me.chanjar.weixin.common.bean.WxAccessToken; +import me.chanjar.weixin.common.bean.WxCardApiSignature; import me.chanjar.weixin.common.bean.WxJsapiSignature; import me.chanjar.weixin.common.bean.WxMenu; import me.chanjar.weixin.common.bean.result.WxError; @@ -28,13 +31,7 @@ import me.chanjar.weixin.common.util.StringUtils; import me.chanjar.weixin.common.util.crypto.SHA1; import me.chanjar.weixin.common.util.crypto.WxCryptUtil; import me.chanjar.weixin.common.util.fs.FileUtils; -import me.chanjar.weixin.common.util.http.MediaDownloadRequestExecutor; -import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor; -import me.chanjar.weixin.common.util.http.RequestExecutor; -import me.chanjar.weixin.common.util.http.SimpleGetRequestExecutor; -import me.chanjar.weixin.common.util.http.SimplePostRequestExecutor; -import me.chanjar.weixin.common.util.http.URIUtil; -import me.chanjar.weixin.common.util.http.Utf8ResponseHandler; +import me.chanjar.weixin.common.util.http.*; import me.chanjar.weixin.common.util.json.GsonHelper; import me.chanjar.weixin.common.util.json.WxGsonBuilder; import me.chanjar.weixin.common.util.xml.XStreamInitializer; @@ -49,6 +46,7 @@ import me.chanjar.weixin.mp.bean.WxMpMaterialArticleUpdate; import me.chanjar.weixin.mp.bean.WxMpMaterialNews; import me.chanjar.weixin.mp.bean.WxMpSemanticQuery; import me.chanjar.weixin.mp.bean.WxMpTemplateMessage; +import me.chanjar.weixin.mp.bean.result.WxMpCardResult; import me.chanjar.weixin.mp.bean.result.WxMpMassSendResult; import me.chanjar.weixin.mp.bean.result.WxMpMassUploadResult; import me.chanjar.weixin.mp.bean.result.WxMpMaterialCountResult; @@ -58,6 +56,7 @@ import me.chanjar.weixin.mp.bean.result.WxMpMaterialUploadResult; import me.chanjar.weixin.mp.bean.result.WxMpMaterialVideoInfoResult; import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken; import me.chanjar.weixin.mp.bean.result.WxMpPayCallback; +import me.chanjar.weixin.mp.bean.result.WxMpPayRefundResult; import me.chanjar.weixin.mp.bean.result.WxMpPayResult; import me.chanjar.weixin.mp.bean.result.WxMpPrepayIdResult; import me.chanjar.weixin.mp.bean.result.WxMpQrCodeTicket; @@ -99,6 +98,7 @@ import org.slf4j.helpers.MessageFormatter; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import com.google.gson.JsonParser; import com.google.gson.internal.Streams; import com.google.gson.reflect.TypeToken; import com.google.gson.stream.JsonReader; @@ -118,6 +118,11 @@ public class WxMpServiceImpl implements WxMpService { */ protected final Object globalJsapiTicketRefreshLock = new Object(); + /** + * 全局的是否正在刷新卡券api_ticket的锁 + */ + protected final Object globalCardApiTicketRefreshLock = new Object(); + protected WxMpConfigStorage wxMpConfigStorage; protected CloseableHttpClient httpClient; @@ -158,14 +163,15 @@ public class WxMpServiceImpl implements WxMpService { RequestConfig config = RequestConfig.custom().setProxy(httpProxy).build(); httpGet.setConfig(config); } - CloseableHttpResponse response = getHttpclient().execute(httpGet); - String resultContent = new BasicResponseHandler().handleResponse(response); - WxError error = WxError.fromJson(resultContent); - if (error.getErrorCode() != 0) { - throw new WxErrorException(error); + try (CloseableHttpResponse response = getHttpclient().execute(httpGet)) { + String resultContent = new BasicResponseHandler().handleResponse(response); + WxError error = WxError.fromJson(resultContent); + if (error.getErrorCode() != 0) { + throw new WxErrorException(error); + } + WxAccessToken accessToken = WxAccessToken.fromJson(resultContent); + wxMpConfigStorage.updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn()); } - WxAccessToken accessToken = WxAccessToken.fromJson(resultContent); - wxMpConfigStorage.updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn()); } catch (ClientProtocolException e) { throw new RuntimeException(e); } catch (IOException e) { @@ -189,7 +195,7 @@ public class WxMpServiceImpl implements WxMpService { synchronized (globalJsapiTicketRefreshLock) { if (wxMpConfigStorage.isJsapiTicketExpired()) { String url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi"; - String responseContent = execute(new SimpleGetRequestExecutor(), url, null); + String responseContent = execute(new JoddGetRequestExecutor(), url, null); JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent))); JsonObject tmpJsonObject = tmpJsonElement.getAsJsonObject(); String jsapiTicket = tmpJsonObject.get("ticket").getAsString(); @@ -226,33 +232,33 @@ public class WxMpServiceImpl implements WxMpService { public void customMessageSend(WxMpCustomMessage message) throws WxErrorException { String url = "https://api.weixin.qq.com/cgi-bin/message/custom/send"; - execute(new SimplePostRequestExecutor(), url, message.toJson()); + execute(new JoddPostRequestExecutor(), url, message.toJson()); } public void menuCreate(WxMenu menu) throws WxErrorException { if (menu.getMatchRule() != null) { String url = "https://api.weixin.qq.com/cgi-bin/menu/addconditional"; - execute(new SimplePostRequestExecutor(), url, menu.toJson()); + execute(new JoddPostRequestExecutor(), url, menu.toJson()); } else { String url = "https://api.weixin.qq.com/cgi-bin/menu/create"; - execute(new SimplePostRequestExecutor(), url, menu.toJson()); + execute(new JoddPostRequestExecutor(), url, menu.toJson()); } } public void menuDelete() throws WxErrorException { String url = "https://api.weixin.qq.com/cgi-bin/menu/delete"; - execute(new SimpleGetRequestExecutor(), url, null); + execute(new JoddGetRequestExecutor(), url, null); } public void menuDelete(String menuid) throws WxErrorException { String url = "https://api.weixin.qq.com/cgi-bin/menu/delconditional"; - execute(new SimpleGetRequestExecutor(), url, "menuid=" + menuid); + execute(new JoddGetRequestExecutor(), url, "menuid=" + menuid); } public WxMenu menuGet() throws WxErrorException { String url = "https://api.weixin.qq.com/cgi-bin/menu/get"; try { - String resultContent = execute(new SimpleGetRequestExecutor(), url, null); + String resultContent = execute(new JoddGetRequestExecutor(), url, null); return WxMenu.fromJson(resultContent); } catch (WxErrorException e) { // 46003 不存在的菜单数据 @@ -266,7 +272,7 @@ public class WxMpServiceImpl implements WxMpService { public WxMenu menuTryMatch(String userid) throws WxErrorException { String url = "https://api.weixin.qq.com/cgi-bin/menu/trymatch"; try { - String resultContent = execute(new SimpleGetRequestExecutor(), url, "user_id=" + userid); + String resultContent = execute(new JoddGetRequestExecutor(), url, "user_id=" + userid); return WxMenu.fromJson(resultContent); } catch (WxErrorException e) { // 46003 不存在的菜单数据 46002 不存在的菜单版本 @@ -379,25 +385,25 @@ public class WxMpServiceImpl implements WxMpService { public WxMpMassUploadResult massNewsUpload(WxMpMassNews news) throws WxErrorException { String url = "https://api.weixin.qq.com/cgi-bin/media/uploadnews"; - String responseContent = execute(new SimplePostRequestExecutor(), url, news.toJson()); + String responseContent = execute(new JoddPostRequestExecutor(), url, news.toJson()); return WxMpMassUploadResult.fromJson(responseContent); } public WxMpMassUploadResult massVideoUpload(WxMpMassVideo video) throws WxErrorException { String url = "http://file.api.weixin.qq.com/cgi-bin/media/uploadvideo"; - String responseContent = execute(new SimplePostRequestExecutor(), url, video.toJson()); + String responseContent = execute(new JoddPostRequestExecutor(), url, video.toJson()); return WxMpMassUploadResult.fromJson(responseContent); } public WxMpMassSendResult massGroupMessageSend(WxMpMassGroupMessage message) throws WxErrorException { String url = "https://api.weixin.qq.com/cgi-bin/message/mass/sendall"; - String responseContent = execute(new SimplePostRequestExecutor(), url, message.toJson()); + String responseContent = execute(new JoddPostRequestExecutor(), url, message.toJson()); return WxMpMassSendResult.fromJson(responseContent); } public WxMpMassSendResult massOpenIdsMessageSend(WxMpMassOpenIdsMessage message) throws WxErrorException { String url = "https://api.weixin.qq.com/cgi-bin/message/mass/send"; - String responseContent = execute(new SimplePostRequestExecutor(), url, message.toJson()); + String responseContent = execute(new JoddPostRequestExecutor(), url, message.toJson()); return WxMpMassSendResult.fromJson(responseContent); } @@ -409,7 +415,7 @@ public class WxMpServiceImpl implements WxMpService { groupJson.addProperty("name", name); String responseContent = execute( - new SimplePostRequestExecutor(), + new JoddPostRequestExecutor(), url, json.toString()); return WxMpGroup.fromJson(responseContent); @@ -417,7 +423,7 @@ public class WxMpServiceImpl implements WxMpService { public List groupGet() throws WxErrorException { String url = "https://api.weixin.qq.com/cgi-bin/groups/get"; - String responseContent = execute(new SimpleGetRequestExecutor(), url, null); + String responseContent = execute(new JoddGetRequestExecutor(), url, null); /* * 操蛋的微信API,创建时返回的是 { group : { id : ..., name : ...} } * 查询时返回的是 { groups : [ { id : ..., name : ..., count : ... }, ... ] } @@ -432,14 +438,14 @@ public class WxMpServiceImpl implements WxMpService { String url = "https://api.weixin.qq.com/cgi-bin/groups/getid"; JsonObject o = new JsonObject(); o.addProperty("openid", openid); - String responseContent = execute(new SimplePostRequestExecutor(), url, o.toString()); + String responseContent = execute(new JoddPostRequestExecutor(), url, o.toString()); JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent))); return GsonHelper.getAsLong(tmpJsonElement.getAsJsonObject().get("groupid")); } public void groupUpdate(WxMpGroup group) throws WxErrorException { String url = "https://api.weixin.qq.com/cgi-bin/groups/update"; - execute(new SimplePostRequestExecutor(), url, group.toJson()); + execute(new JoddPostRequestExecutor(), url, group.toJson()); } public void userUpdateGroup(String openid, long to_groupid) throws WxErrorException { @@ -447,7 +453,7 @@ public class WxMpServiceImpl implements WxMpService { JsonObject json = new JsonObject(); json.addProperty("openid", openid); json.addProperty("to_groupid", to_groupid); - execute(new SimplePostRequestExecutor(), url, json.toString()); + execute(new JoddPostRequestExecutor(), url, json.toString()); } public void userUpdateRemark(String openid, String remark) throws WxErrorException { @@ -455,19 +461,19 @@ public class WxMpServiceImpl implements WxMpService { JsonObject json = new JsonObject(); json.addProperty("openid", openid); json.addProperty("remark", remark); - execute(new SimplePostRequestExecutor(), url, json.toString()); + execute(new JoddPostRequestExecutor(), url, json.toString()); } public WxMpUser userInfo(String openid, String lang) throws WxErrorException { String url = "https://api.weixin.qq.com/cgi-bin/user/info"; lang = lang == null ? "zh_CN" : lang; - String responseContent = execute(new SimpleGetRequestExecutor(), url, "openid=" + openid + "&lang=" + lang); + String responseContent = execute(new JoddGetRequestExecutor(), url, "openid=" + openid + "&lang=" + lang); return WxMpUser.fromJson(responseContent); } public WxMpUserList userList(String next_openid) throws WxErrorException { String url = "https://api.weixin.qq.com/cgi-bin/user/get"; - String responseContent = execute(new SimpleGetRequestExecutor(), url, next_openid == null ? null : "next_openid=" + next_openid); + String responseContent = execute(new JoddGetRequestExecutor(), url, next_openid == null ? null : "next_openid=" + next_openid); return WxMpUserList.fromJson(responseContent); } @@ -483,7 +489,7 @@ public class WxMpServiceImpl implements WxMpService { scene.addProperty("scene_id", scene_id); actionInfo.add("scene", scene); json.add("action_info", actionInfo); - String responseContent = execute(new SimplePostRequestExecutor(), url, json.toString()); + String responseContent = execute(new JoddPostRequestExecutor(), url, json.toString()); return WxMpQrCodeTicket.fromJson(responseContent); } @@ -496,7 +502,7 @@ public class WxMpServiceImpl implements WxMpService { scene.addProperty("scene_id", scene_id); actionInfo.add("scene", scene); json.add("action_info", actionInfo); - String responseContent = execute(new SimplePostRequestExecutor(), url, json.toString()); + String responseContent = execute(new JoddPostRequestExecutor(), url, json.toString()); return WxMpQrCodeTicket.fromJson(responseContent); } @@ -509,7 +515,7 @@ public class WxMpServiceImpl implements WxMpService { scene.addProperty("scene_str", scene_str); actionInfo.add("scene", scene); json.add("action_info", actionInfo); - String responseContent = execute(new SimplePostRequestExecutor(), url, json.toString()); + String responseContent = execute(new JoddPostRequestExecutor(), url, json.toString()); return WxMpQrCodeTicket.fromJson(responseContent); } @@ -523,14 +529,14 @@ public class WxMpServiceImpl implements WxMpService { JsonObject o = new JsonObject(); o.addProperty("action", "long2short"); o.addProperty("long_url", long_url); - String responseContent = execute(new SimplePostRequestExecutor(), url, o.toString()); + String responseContent = execute(new JoddPostRequestExecutor(), url, o.toString()); JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent))); return tmpJsonElement.getAsJsonObject().get("short_url").getAsString(); } public String templateSend(WxMpTemplateMessage templateMessage) throws WxErrorException { String url = "https://api.weixin.qq.com/cgi-bin/message/template/send"; - String responseContent = execute(new SimplePostRequestExecutor(), url, templateMessage.toJson()); + String responseContent = execute(new JoddPostRequestExecutor(), url, templateMessage.toJson()); JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent))); final JsonObject jsonObject = tmpJsonElement.getAsJsonObject(); if (jsonObject.get("errcode").getAsInt() == 0) @@ -540,7 +546,7 @@ public class WxMpServiceImpl implements WxMpService { public WxMpSemanticQueryResult semanticQuery(WxMpSemanticQuery semanticQuery) throws WxErrorException { String url = "https://api.weixin.qq.com/semantic/semproxy/search"; - String responseContent = execute(new SimplePostRequestExecutor(), url, semanticQuery.toJson()); + String responseContent = execute(new JoddPostRequestExecutor(), url, semanticQuery.toJson()); return WxMpSemanticQueryResult.fromJson(responseContent); } @@ -572,7 +578,7 @@ public class WxMpServiceImpl implements WxMpService { url += "&grant_type=authorization_code"; try { - RequestExecutor executor = new SimpleGetRequestExecutor(); + RequestExecutor executor = new JoddGetRequestExecutor(); String responseText = executor.execute(getHttpclient(), httpProxy, url, null); return WxMpOAuth2AccessToken.fromJson(responseText); } catch (ClientProtocolException e) { @@ -590,7 +596,7 @@ public class WxMpServiceImpl implements WxMpService { url += "&refresh_token=" + refreshToken; try { - RequestExecutor executor = new SimpleGetRequestExecutor(); + RequestExecutor executor = new JoddGetRequestExecutor(); String responseText = executor.execute(getHttpclient(), httpProxy, url, null); return WxMpOAuth2AccessToken.fromJson(responseText); } catch (ClientProtocolException e) { @@ -612,7 +618,7 @@ public class WxMpServiceImpl implements WxMpService { } try { - RequestExecutor executor = new SimpleGetRequestExecutor(); + RequestExecutor executor = new JoddGetRequestExecutor(); String responseText = executor.execute(getHttpclient(), httpProxy, url, null); return WxMpUser.fromJson(responseText); } catch (ClientProtocolException e) { @@ -629,7 +635,7 @@ public class WxMpServiceImpl implements WxMpService { url += "&openid=" + oAuth2AccessToken.getOpenId(); try { - RequestExecutor executor = new SimpleGetRequestExecutor(); + RequestExecutor executor = new JoddGetRequestExecutor(); executor.execute(getHttpclient(), httpProxy, url, null); } catch (ClientProtocolException e) { throw new RuntimeException(e); @@ -682,11 +688,11 @@ public class WxMpServiceImpl implements WxMpService { } public String get(String url, String queryParam) throws WxErrorException { - return execute(new SimpleGetRequestExecutor(), url, queryParam); + return execute(new JoddGetRequestExecutor(), url, queryParam); } public String post(String url, String postData) throws WxErrorException { - return execute(new SimplePostRequestExecutor(), url, postData); + return execute(new JoddPostRequestExecutor(), url, postData); } /** @@ -851,8 +857,7 @@ public class WxMpServiceImpl implements WxMpService { StringEntity entity = new StringEntity(request.toString(), Consts.UTF_8); httpPost.setEntity(entity); - try { - CloseableHttpResponse response = getHttpclient().execute(httpPost); + try(CloseableHttpResponse response = getHttpclient().execute(httpPost)) { String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response); XStream xstream = XStreamInitializer.getInstance(); xstream.alias("xml", WxMpPrepayIdResult.class); @@ -878,13 +883,14 @@ public class WxMpServiceImpl implements WxMpService { } @Override - public Map getJSSDKPayInfo(String openId, String outTradeNo, double amt, String body, String tradeType, String ip, String callbackUrl) { + public Map getJSSDKPayInfo(String openId, String outTradeNo, double amt, String body, String tradeType, String ip, String callbackUrl) + throws WxErrorException { Map packageParams = new HashMap(); packageParams.put("appid", wxMpConfigStorage.getAppId()); packageParams.put("mch_id", wxMpConfigStorage.getPartnerId()); packageParams.put("body", body); packageParams.put("out_trade_no", outTradeNo); - packageParams.put("total_fee", (int) (amt * 100) + ""); + packageParams.put("total_fee", String.format("%.0f", amt * 100)); packageParams.put("spbill_create_ip", ip); packageParams.put("notify_url", callbackUrl); packageParams.put("trade_type", tradeType); @@ -894,8 +900,21 @@ public class WxMpServiceImpl implements WxMpService { } @Override - public Map getJSSDKPayInfo(Map parameters) { + public Map getJSSDKPayInfo(Map parameters) throws WxErrorException { WxMpPrepayIdResult wxMpPrepayIdResult = getPrepayId(parameters); + + if (!"SUCCESS".equalsIgnoreCase(wxMpPrepayIdResult.getReturn_code()) + ||!"SUCCESS".equalsIgnoreCase(wxMpPrepayIdResult.getResult_code())) { + WxError error = new WxError(); + error.setErrorCode(-1); + error.setErrorMsg("return_code:" + wxMpPrepayIdResult.getReturn_code() + + ";return_msg:" + wxMpPrepayIdResult.getReturn_msg() + + ";result_code:" + wxMpPrepayIdResult.getResult_code() + + ";err_code" + wxMpPrepayIdResult.getErr_code() + + ";err_code_des" + wxMpPrepayIdResult.getErr_code_des()); + throw new WxErrorException(error); + } + String prepayId = wxMpPrepayIdResult.getPrepay_id(); if (prepayId == null || prepayId.equals("")) { throw new RuntimeException(String.format("Failed to get prepay id due to error code '%s'(%s).", wxMpPrepayIdResult.getErr_code(), wxMpPrepayIdResult.getErr_code_des())); @@ -944,8 +963,7 @@ public class WxMpServiceImpl implements WxMpService { StringEntity entity = new StringEntity(request.toString(), Consts.UTF_8); httpPost.setEntity(entity); - try { - CloseableHttpResponse response = httpClient.execute(httpPost); + try(CloseableHttpResponse response = httpClient.execute(httpPost)) { String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response); XStream xstream = XStreamInitializer.getInstance(); xstream.alias("xml", WxMpPayResult.class); @@ -969,6 +987,59 @@ public class WxMpServiceImpl implements WxMpService { return new WxMpPayCallback(); } + @Override + public WxMpPayRefundResult refundPay(Map parameters) throws WxErrorException { + SortedMap refundParams = new TreeMap(parameters); + refundParams.put("appid", wxMpConfigStorage.getAppId()); + refundParams.put("mch_id", wxMpConfigStorage.getPartnerId()); + refundParams.put("nonce_str", System.currentTimeMillis() + ""); + refundParams.put("op_user_id", wxMpConfigStorage.getPartnerId()); + String sign = WxCryptUtil.createSign(refundParams, wxMpConfigStorage.getPartnerKey()); + refundParams.put("sign", sign); + + StringBuilder request = new StringBuilder(""); + for (Entry para : refundParams.entrySet()) { + request.append(String.format("<%s>%s", para.getKey(), para.getValue(), para.getKey())); + } + request.append(""); + + HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/secapi/pay/refund"); + if (httpProxy != null) { + RequestConfig config = RequestConfig.custom().setProxy(httpProxy).build(); + httpPost.setConfig(config); + } + + StringEntity entity = new StringEntity(request.toString(), Consts.UTF_8); + httpPost.setEntity(entity); + try( + CloseableHttpResponse response = getHttpclient().execute(httpPost)) { + String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response); + XStream xstream = XStreamInitializer.getInstance(); + xstream.processAnnotations(WxMpPayRefundResult.class); + WxMpPayRefundResult wxMpPayRefundResult = (WxMpPayRefundResult) xstream.fromXML(responseContent); + + if (!"SUCCESS".equalsIgnoreCase(wxMpPayRefundResult.getResultCode()) + ||!"SUCCESS".equalsIgnoreCase(wxMpPayRefundResult.getReturnCode())) { + WxError error = new WxError(); + error.setErrorCode(-1); + error.setErrorMsg("return_code:" + wxMpPayRefundResult.getReturnCode() + + ";return_msg:" + wxMpPayRefundResult.getReturnMsg() + + ";result_code:" + wxMpPayRefundResult.getResultCode() + + ";err_code" + wxMpPayRefundResult.getErrCode() + + ";err_code_des" + wxMpPayRefundResult.getErrCodeDes()); + throw new WxErrorException(error); + } + + return wxMpPayRefundResult; + } catch (IOException e) { + log.error(MessageFormatter.format("The exception was happened when sending refund '{}'.", request.toString()).getMessage(), e); + WxError error = new WxError(); + error.setErrorCode(-1); + error.setErrorMsg("incorrect response."); + throw new WxErrorException(error); + } + } + @Override public boolean checkJSSDKCallbackDataSignature(Map kvm, String signature) { return signature.equals(WxCryptUtil.createSign(kvm, wxMpConfigStorage.getPartnerKey())); @@ -1000,8 +1071,7 @@ public class WxMpServiceImpl implements WxMpService { StringEntity entity = new StringEntity(request.toString(), Consts.UTF_8); httpPost.setEntity(entity); - try { - CloseableHttpResponse response = getHttpclient().execute(httpPost); + try(CloseableHttpResponse response = getHttpclient().execute(httpPost)) { String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response); XStream xstream = XStreamInitializer.getInstance(); xstream.processAnnotations(WxRedpackResult.class); @@ -1014,4 +1084,201 @@ public class WxMpServiceImpl implements WxMpService { throw new WxErrorException(error); } } + + /** + * 获得卡券api_ticket,不强制刷新卡券api_ticket + * + * @return 卡券api_ticket + * @throws WxErrorException + * @see #getCardApiTicket(boolean) + */ + @Override + public String getCardApiTicket() throws WxErrorException { + return getCardApiTicket(false); + } + + /** + *
    +   * 获得卡券api_ticket
    +   * 获得时会检查卡券apiToken是否过期,如果过期了,那么就刷新一下,否则就什么都不干
    +   *
    +   * 详情请见:http://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html#.E9.99.84.E5.BD
    +   * .954-.E5.8D.A1.E5.88.B8.E6.89.A9.E5.B1.95.E5.AD.97.E6.AE.B5.E5.8F.8A.E7.AD.BE.E5.90.8D.E7.94
    +   * .9F.E6.88.90.E7.AE.97.E6.B3.95
    +   * 
    + * + * @param forceRefresh 强制刷新 + * @return 卡券api_ticket + * @throws WxErrorException + */ + @Override + public String getCardApiTicket(boolean forceRefresh) throws WxErrorException { + if (forceRefresh) { + wxMpConfigStorage.expireCardApiTicket(); + } + if (wxMpConfigStorage.isCardApiTicketExpired()) { + synchronized (globalCardApiTicketRefreshLock) { + if (wxMpConfigStorage.isCardApiTicketExpired()) { + String url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=wx_card"; + String responseContent = execute(new JoddGetRequestExecutor(), url, null); + JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent))); + JsonObject tmpJsonObject = tmpJsonElement.getAsJsonObject(); + String cardApiTicket = tmpJsonObject.get("ticket").getAsString(); + int expiresInSeconds = tmpJsonObject.get("expires_in").getAsInt(); + wxMpConfigStorage.updateCardApiTicket(cardApiTicket, expiresInSeconds); + } + } + } + return wxMpConfigStorage.getCardApiTicket(); + } + + /** + *
    +   * 创建调用卡券api时所需要的签名
    +   *
    +   * 详情请见:http://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html#.E9.99.84.E5.BD
    +   * .954-.E5.8D.A1.E5.88.B8.E6.89.A9.E5.B1.95.E5.AD.97.E6.AE.B5.E5.8F.8A.E7.AD.BE.E5.90.8D.E7.94
    +   * .9F.E6.88.90.E7.AE.97.E6.B3.95
    +   * 
    + * + * @param optionalSignParam 参与签名的参数数组。 + * 可以为下列字段:app_id, card_id, card_type, code, openid, location_id + *
    注意:当做wx.chooseCard调用时,必须传入app_id参与签名,否则会造成签名失败导致拉取卡券列表为空 + * @return 卡券Api签名对象 + */ + @Override + public WxCardApiSignature createCardApiSignature(String... optionalSignParam) throws + WxErrorException { + long timestamp = System.currentTimeMillis() / 1000; + String nonceStr = RandomUtils.getRandomStr(); + String cardApiTicket = getCardApiTicket(false); + + String[] signParam = Arrays.copyOf(optionalSignParam, optionalSignParam.length + 3); + signParam[optionalSignParam.length] = String.valueOf(timestamp); + signParam[optionalSignParam.length + 1] = nonceStr; + signParam[optionalSignParam.length + 2] = cardApiTicket; + try { + String signature = SHA1.gen(signParam); + WxCardApiSignature cardApiSignature = new WxCardApiSignature(); + cardApiSignature.setTimestamp(timestamp); + cardApiSignature.setNonceStr(nonceStr); + cardApiSignature.setSignature(signature); + return cardApiSignature; + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + } + + /** + * 卡券Code解码 + * + * @param encryptCode 加密Code,通过JSSDK的chooseCard接口获得 + * @return 解密后的Code + * @throws WxErrorException + */ + @Override + public String decryptCardCode(String encryptCode) throws WxErrorException { + String url = "https://api.weixin.qq.com/card/code/decrypt"; + JsonObject param = new JsonObject(); + param.addProperty("encrypt_code", encryptCode); + String responseContent = post(url, param.toString()); + JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent))); + JsonObject tmpJsonObject = tmpJsonElement.getAsJsonObject(); + JsonPrimitive jsonPrimitive = tmpJsonObject.getAsJsonPrimitive("code"); + return jsonPrimitive.getAsString(); + } + + /** + * 卡券Code查询 + * + * @param cardId 卡券ID代表一类卡券 + * @param code 单张卡券的唯一标准 + * @param checkConsume 是否校验code核销状态,填入true和false时的code异常状态返回数据不同 + * @return WxMpCardResult对象 + * @throws WxErrorException + */ + @Override + public WxMpCardResult queryCardCode(String cardId, String code, boolean checkConsume) throws WxErrorException { + String url = "https://api.weixin.qq.com/card/code/get"; + JsonObject param = new JsonObject(); + param.addProperty("card_id", cardId); + param.addProperty("code", code); + param.addProperty("check_consume", checkConsume); + String responseContent = post(url, param.toString()); + JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent))); + return WxMpGsonBuilder.INSTANCE.create().fromJson(tmpJsonElement, + new TypeToken() { + }.getType()); + } + + /** + * 卡券Code核销。核销失败会抛出异常 + * + * @param code 单张卡券的唯一标准 + * @throws WxErrorException + */ + @Override + public String consumeCardCode(String code, String cardId) throws WxErrorException { + String url = "https://api.weixin.qq.com/card/code/consume"; + JsonObject param = new JsonObject(); + param.addProperty("code", code); + + if (cardId != null && !"".equals(cardId)) { + param.addProperty("card_id", cardId); + } + + String responseContent = post(url, param.toString()); + return responseContent; + } + + /** + * 卡券Mark接口。 + * 开发者在帮助消费者核销卡券之前,必须帮助先将此code(卡券串码)与一个openid绑定(即mark住), + * 才能进一步调用核销接口,否则报错。 + * + * @param code 卡券的code码 + * @param cardId 卡券的ID + * @param openId 用券用户的openid + * @param isMark 是否要mark(占用)这个code,填写true或者false,表示占用或解除占用 + * @throws WxErrorException + */ + @Override + public void markCardCode(String code, String cardId, String openId, boolean isMark) throws + WxErrorException { + String url = "https://api.weixin.qq.com/card/code/mark"; + JsonObject param = new JsonObject(); + param.addProperty("code", code); + param.addProperty("card_id", cardId); + param.addProperty("openid", openId); + param.addProperty("is_mark", isMark); + String responseContent = post(url, param.toString()); + JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent))); + WxMpCardResult cardResult = WxMpGsonBuilder.INSTANCE.create().fromJson(tmpJsonElement, + new TypeToken() { }.getType()); + if (!cardResult.getErrorCode().equals("0")) { + log.warn("朋友的券mark失败:{}", cardResult.getErrorMsg()); + } + } + + @Override + public String getCardDetail(String cardId) throws WxErrorException { + String url = "https://api.weixin.qq.com/card/get"; + JsonObject param = new JsonObject(); + param.addProperty("card_id", cardId); + String responseContent = post(url, param.toString()); + + // 判断返回值 + JsonObject json = (new JsonParser()).parse(responseContent).getAsJsonObject(); + String errcode = json.get("errcode").getAsString(); + if (!"0".equals(errcode)) { + String errmsg = json.get("errmsg").getAsString(); + WxError error = new WxError(); + error.setErrorCode(Integer.valueOf(errcode)); + error.setErrorMsg(errmsg); + throw new WxErrorException(error); + } + + return responseContent; + } + } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpCard.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpCard.java new file mode 100644 index 000000000..d530fbe82 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpCard.java @@ -0,0 +1,72 @@ +package me.chanjar.weixin.mp.bean; + + +/** + * 微信卡券 + * + * @author YuJian + * @version 15/11/11 + */ +public class WxMpCard { + + private String cardId; + + private Long beginTime; + + private Long endTime; + + private String userCardStatus; + + private Boolean canConsume; + + public String getCardId() { + return cardId; + } + + public void setCardId(String cardId) { + this.cardId = cardId; + } + + 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; + } + + public String getUserCardStatus() { + return userCardStatus; + } + + public void setUserCardStatus(String userCardStatus) { + this.userCardStatus = userCardStatus; + } + + public Boolean getCanConsume() { + return canConsume; + } + + public void setCanConsume(Boolean canConsume) { + this.canConsume = canConsume; + } + + @Override + public String toString() { + return "WxMpCard{" + + "cardId='" + cardId + '\'' + + ", beginTime=" + beginTime + + ", endTime=" + endTime + + ", userCardStatus='" + userCardStatus + '\'' + + ", canConsume=" + canConsume + + '}'; + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpXmlMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpXmlMessage.java index 8796b1105..9f42ffc70 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpXmlMessage.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpXmlMessage.java @@ -150,6 +150,31 @@ public class WxMpXmlMessage implements Serializable { @XStreamAlias("ErrorCount") private Integer errorCount; + /////////////////////////////////////// + // 卡券相关事件推送 + /////////////////////////////////////// + @XStreamAlias("CardId") + @XStreamConverter(value=XStreamCDataConverter.class) + private String cardId; + + @XStreamAlias("FriendUserName") + @XStreamConverter(value=XStreamCDataConverter.class) + private String friendUserName; + + @XStreamAlias("IsGiveByFriend") + private Integer isGiveByFriend; // 是否为转赠,1代表是,0代表否 + + @XStreamAlias("UserCardCode") + @XStreamConverter(value=XStreamCDataConverter.class) + private String userCardCode; + + @XStreamAlias("OldUserCardCode") + @XStreamConverter(value=XStreamCDataConverter.class) + private String oldUserCardCode; + + @XStreamAlias("OuterId") + private Integer outerId; + @XStreamAlias("ScanCodeInfo") private ScanCodeInfo scanCodeInfo = new ScanCodeInfo(); @@ -456,6 +481,54 @@ public class WxMpXmlMessage implements Serializable { this.errorCount = errorCount; } + public String getCardId() { + return cardId; + } + + public void setCardId(String cardId) { + this.cardId = cardId; + } + + public String getFriendUserName() { + return friendUserName; + } + + public void setFriendUserName(String friendUserName) { + this.friendUserName = friendUserName; + } + + public Integer getIsGiveByFriend() { + return isGiveByFriend; + } + + public void setIsGiveByFriend(Integer isGiveByFriend) { + this.isGiveByFriend = isGiveByFriend; + } + + public String getUserCardCode() { + return userCardCode; + } + + public void setUserCardCode(String userCardCode) { + this.userCardCode = userCardCode; + } + + public String getOldUserCardCode() { + return oldUserCardCode; + } + + public void setOldUserCardCode(String oldUserCardCode) { + this.oldUserCardCode = oldUserCardCode; + } + + public Integer getOuterId() { + return outerId; + } + + public void setOuterId(Integer outerId) { + this.outerId = outerId; + } + public WxMpXmlMessage.ScanCodeInfo getScanCodeInfo() { return scanCodeInfo; } @@ -652,6 +725,11 @@ public class WxMpXmlMessage implements Serializable { ", filterCount=" + filterCount + ", sentCount=" + sentCount + ", errorCount=" + errorCount + + ", cardId='" + cardId + '\'' + + ", isGiveByFriend=" + isGiveByFriend + + ", userCardCode='" + userCardCode + '\'' + + ", oldUserCardCode='" + oldUserCardCode + '\'' + + ", outerId=" + outerId + ", scanCodeInfo=" + scanCodeInfo + ", sendPicsInfo=" + sendPicsInfo + ", sendLocationInfo=" + sendLocationInfo + diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpCardResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpCardResult.java new file mode 100644 index 000000000..e6cd9a5f8 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpCardResult.java @@ -0,0 +1,87 @@ +package me.chanjar.weixin.mp.bean.result; + +import me.chanjar.weixin.mp.bean.WxMpCard; + +import java.io.Serializable; + +/** + * 卡券查询Code,核销Code接口返回结果 + * + * @author YuJian + * @version 15/11/11 + */ +public class WxMpCardResult implements Serializable { + + private String errorCode; + + private String errorMsg; + + private String openId; + + private WxMpCard card; + + private String userCardStatus; + + private Boolean canConsume; + + public String getErrorCode() { + return errorCode; + } + + public void setErrorCode(String errorCode) { + this.errorCode = errorCode; + } + + public String getErrorMsg() { + return errorMsg; + } + + public void setErrorMsg(String errorMsg) { + this.errorMsg = errorMsg; + } + + public String getOpenId() { + return openId; + } + + public void setOpenId(String openId) { + this.openId = openId; + } + + public WxMpCard getCard() { + return card; + } + + public void setCard(WxMpCard card) { + this.card = card; + } + + @Override + public String toString() { + return "WxMpCardResult{" + + "errorCode='" + errorCode + '\'' + + ", errorMsg='" + errorMsg + '\'' + + ", openId='" + openId + '\'' + + ", card=" + card + + ", userCardStatus='" + userCardStatus + '\'' + + ", canConsume=" + canConsume + + '}'; + } + + public String getUserCardStatus() { + return userCardStatus; + } + + public void setUserCardStatus(String userCardStatus) { + this.userCardStatus = userCardStatus; + } + + public Boolean getCanConsume() { + return canConsume; + } + + public void setCanConsume(Boolean canConsume) { + this.canConsume = canConsume; + } + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpPayRefundResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpPayRefundResult.java new file mode 100644 index 000000000..1e8327e30 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpPayRefundResult.java @@ -0,0 +1,278 @@ +package me.chanjar.weixin.mp.bean.result; + +import java.io.Serializable; + +import com.thoughtworks.xstream.annotations.XStreamAlias; + +/** + * 微信支付-申请退款返回结果 + * https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_4 + * @author liukaitj + * + */ +@XStreamAlias("xml") +public class WxMpPayRefundResult implements Serializable { + + private static final long serialVersionUID = 1L; + + @XStreamAlias("return_code") + private String returnCode; + + @XStreamAlias("return_msg") + private String returnMsg; + + @XStreamAlias("result_code") + private String resultCode; + + @XStreamAlias("err_code") + private String errCode; + + @XStreamAlias("err_code_des") + private String errCodeDes; + + @XStreamAlias("appid") + private String appid; + + @XStreamAlias("mch_id") + private String mchId; + + @XStreamAlias("device_info") + private String deviceInfo; + + @XStreamAlias("nonce_str") + private String nonceStr; + + @XStreamAlias("sign") + private String sign; + + @XStreamAlias("transaction_id") + private String transactionId; + + @XStreamAlias("out_trade_no") + private String outTradeNo; + + @XStreamAlias("out_refund_no") + private String outRefundNo; + + @XStreamAlias("refund_id") + private String refundId; + + @XStreamAlias("refund_channel") + private String refundChannel; + + @XStreamAlias("refund_fee") + private String refundFee; + + @XStreamAlias("total_fee") + private String totalFee; + + @XStreamAlias("fee_type") + private String feeType; + + @XStreamAlias("cash_fee") + private String cashFee; + + @XStreamAlias("cash_refund_fee") + private String cashRefundfee; + + @XStreamAlias("coupon_refund_fee") + private String couponRefundFee; + + @XStreamAlias("coupon_refund_count") + private String couponRefundCount; + + @XStreamAlias("coupon_refund_id") + private String couponRefundId; + + public String getReturnCode() { + return returnCode; + } + + public void setReturnCode(String returnCode) { + this.returnCode = returnCode; + } + + public String getReturnMsg() { + return returnMsg; + } + + public void setReturnMsg(String returnMsg) { + this.returnMsg = returnMsg; + } + + public String getResultCode() { + return resultCode; + } + + public void setResultCode(String resultCode) { + this.resultCode = resultCode; + } + + public String getErrCode() { + return errCode; + } + + public void setErrCode(String errCode) { + this.errCode = errCode; + } + + public String getErrCodeDes() { + return errCodeDes; + } + + public void setErrCodeDes(String errCodeDes) { + this.errCodeDes = errCodeDes; + } + + public String getAppid() { + return appid; + } + + public void setAppid(String appid) { + this.appid = appid; + } + + public String getMchId() { + return mchId; + } + + public void setMchId(String mchId) { + this.mchId = mchId; + } + + public String getDeviceInfo() { + return deviceInfo; + } + + public void setDeviceInfo(String deviceInfo) { + this.deviceInfo = deviceInfo; + } + + public String getNonceStr() { + return nonceStr; + } + + public void setNonceStr(String nonceStr) { + this.nonceStr = nonceStr; + } + + public String getSign() { + return sign; + } + + public void setSign(String sign) { + this.sign = sign; + } + + public String getTransactionId() { + return transactionId; + } + + public void setTransactionId(String transactionId) { + this.transactionId = transactionId; + } + + public String getOutTradeNo() { + return outTradeNo; + } + + public void setOutTradeNo(String outTradeNo) { + this.outTradeNo = outTradeNo; + } + + public String getOutRefundNo() { + return outRefundNo; + } + + public void setOutRefundNo(String outRefundNo) { + this.outRefundNo = outRefundNo; + } + + public String getRefundId() { + return refundId; + } + + public void setRefundId(String refundId) { + this.refundId = refundId; + } + + public String getRefundChannel() { + return refundChannel; + } + + public void setRefundChannel(String refundChannel) { + this.refundChannel = refundChannel; + } + + public String getRefundFee() { + return refundFee; + } + + public void setRefundFee(String refundFee) { + this.refundFee = refundFee; + } + + public String getTotalFee() { + return totalFee; + } + + public void setTotalFee(String totalFee) { + this.totalFee = totalFee; + } + + public String getFeeType() { + return feeType; + } + + public void setFeeType(String feeType) { + this.feeType = feeType; + } + + public String getCashFee() { + return cashFee; + } + + public void setCashFee(String cashFee) { + this.cashFee = cashFee; + } + + public String getCashRefundfee() { + return cashRefundfee; + } + + public void setCashRefundfee(String cashRefundfee) { + this.cashRefundfee = cashRefundfee; + } + + public String getCouponRefundFee() { + return couponRefundFee; + } + + public void setCouponRefundFee(String couponRefundFee) { + this.couponRefundFee = couponRefundFee; + } + + public String getCouponRefundCount() { + return couponRefundCount; + } + + public void setCouponRefundCount(String couponRefundCount) { + this.couponRefundCount = couponRefundCount; + } + + public String getCouponRefundId() { + return couponRefundId; + } + + public void setCouponRefundId(String couponRefundId) { + this.couponRefundId = couponRefundId; + } + + @Override + public String toString() { + return "[" + + "return_code:" + returnCode + ";" + + "return_msg" + returnMsg + ";"; + } + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpCardGsonAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpCardGsonAdapter.java new file mode 100644 index 000000000..dab414a3e --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpCardGsonAdapter.java @@ -0,0 +1,38 @@ +package me.chanjar.weixin.mp.util.json; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.reflect.TypeToken; +import me.chanjar.weixin.common.util.json.GsonHelper; +import me.chanjar.weixin.mp.bean.WxMpCard; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.reflect.Type; +import java.util.List; + +/** + * Created by YuJian on 15/11/11. + * + * @author YuJian + * @version 15/11/11 + */ +public class WxMpCardGsonAdapter implements JsonDeserializer { + + @Override + public WxMpCard deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext + jsonDeserializationContext) throws JsonParseException { + WxMpCard card = new WxMpCard(); + JsonObject jsonObject = jsonElement.getAsJsonObject(); + + card.setCardId(GsonHelper.getString(jsonObject, "card_id")); + card.setBeginTime(GsonHelper.getLong(jsonObject, "begin_time")); + card.setEndTime(GsonHelper.getLong(jsonObject, "end_time")); + + return card; + } + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpCardResultGsonAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpCardResultGsonAdapter.java new file mode 100644 index 000000000..be1a12da3 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpCardResultGsonAdapter.java @@ -0,0 +1,45 @@ +package me.chanjar.weixin.mp.util.json; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.reflect.TypeToken; +import me.chanjar.weixin.common.util.json.GsonHelper; +import me.chanjar.weixin.mp.bean.WxMpCard; +import me.chanjar.weixin.mp.bean.result.WxMpCardResult; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.reflect.Type; +import java.text.ParseException; +import java.util.List; + +/** + * Created by YuJian on 15/11/11. + * + * @author YuJian + * @version 15/11/11 + */ +public class WxMpCardResultGsonAdapter implements JsonDeserializer { + @Override + public WxMpCardResult deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { + WxMpCardResult cardResult = new WxMpCardResult(); + JsonObject jsonObject = jsonElement.getAsJsonObject(); + + cardResult.setOpenId(GsonHelper.getString(jsonObject, "openid")); + cardResult.setErrorCode(GsonHelper.getString(jsonObject, "errcode")); + cardResult.setErrorMsg(GsonHelper.getString(jsonObject, "errmsg")); + cardResult.setCanConsume(GsonHelper.getBoolean(jsonObject, "can_consume")); + cardResult.setUserCardStatus(GsonHelper.getString(jsonObject, "user_card_status")); + + WxMpCard card = WxMpGsonBuilder.INSTANCE.create().fromJson(jsonObject.get("card"), + new TypeToken() { + }.getType()); + + cardResult.setCard(card); + + return cardResult; + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpGsonBuilder.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpGsonBuilder.java index 20f8d9d47..316f22f54 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpGsonBuilder.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpGsonBuilder.java @@ -38,6 +38,8 @@ public class WxMpGsonBuilder { INSTANCE.registerTypeAdapter(WxMpMaterialNewsBatchGetResult.WxMaterialNewsBatchGetNewsItem.class, new WxMpMaterialNewsBatchGetGsonItemAdapter()); INSTANCE.registerTypeAdapter(WxMpMaterialFileBatchGetResult.class, new WxMpMaterialFileBatchGetGsonAdapter()); INSTANCE.registerTypeAdapter(WxMpMaterialFileBatchGetResult.WxMaterialFileBatchGetNewsItem.class, new WxMpMaterialFileBatchGetGsonItemAdapter()); + INSTANCE.registerTypeAdapter(WxMpCardResult.class, new WxMpCardResultGsonAdapter()); + INSTANCE.registerTypeAdapter(WxMpCard.class, new WxMpCardGsonAdapter()); } public static Gson create() {