mirror of
https://gitee.com/binary/weixin-java-tools.git
synced 2025-04-24 08:57:16 +08:00
Merge pull request #263 from mgcnrx11/feat_card_coupon
Feature card coupon
This commit is contained in:
commit
b5986cfd4f
@ -47,7 +47,7 @@ public class WxConsts {
|
|||||||
///////////////////////
|
///////////////////////
|
||||||
public static final String MASS_ST_SUCCESS = "send success";
|
public static final String MASS_ST_SUCCESS = "send success";
|
||||||
public static final String MASS_ST_FAIL = "send fail";
|
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(20001)";
|
||||||
public static final String MASS_ST_涉嫌社会 = "err(20004)";
|
public static final String MASS_ST_涉嫌社会 = "err(20004)";
|
||||||
public static final String MASS_ST_涉嫌色情 = "err(20002)";
|
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_LOCATION_SELECT = "location_select";
|
||||||
public static final String EVT_TEMPLATESENDJOBFINISH = "TEMPLATESENDJOBFINISH";
|
public static final String EVT_TEMPLATESENDJOBFINISH = "TEMPLATESENDJOBFINISH";
|
||||||
public static final String EVT_ENTER_AGENT = "enter_agent";
|
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"; // 库存报警
|
||||||
|
|
||||||
///////////////////////
|
///////////////////////
|
||||||
// 上传多媒体文件的类型
|
// 上传多媒体文件的类型
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -50,6 +50,21 @@ public interface WxMpConfigStorage {
|
|||||||
*/
|
*/
|
||||||
public void updateJsapiTicket(String jsapiTicket, int expiresInSeconds);
|
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 getAppId();
|
||||||
|
|
||||||
public String getSecret();
|
public String getSecret();
|
||||||
|
@ -32,6 +32,9 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage {
|
|||||||
protected volatile String jsapiTicket;
|
protected volatile String jsapiTicket;
|
||||||
protected volatile long jsapiTicketExpiresTime;
|
protected volatile long jsapiTicketExpiresTime;
|
||||||
|
|
||||||
|
protected volatile String cardApiTicket;
|
||||||
|
protected volatile long cardApiTicketExpiresTime;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 临时文件目录
|
* 临时文件目录
|
||||||
*/
|
*/
|
||||||
@ -90,6 +93,27 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage {
|
|||||||
this.jsapiTicketExpiresTime = 0;
|
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() {
|
public String getAppId() {
|
||||||
return this.appId;
|
return this.appId;
|
||||||
}
|
}
|
||||||
@ -192,6 +216,8 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage {
|
|||||||
", http_proxy_password='" + http_proxy_password + '\'' +
|
", http_proxy_password='" + http_proxy_password + '\'' +
|
||||||
", jsapiTicket='" + jsapiTicket + '\'' +
|
", jsapiTicket='" + jsapiTicket + '\'' +
|
||||||
", jsapiTicketExpiresTime='" + jsapiTicketExpiresTime + '\'' +
|
", jsapiTicketExpiresTime='" + jsapiTicketExpiresTime + '\'' +
|
||||||
|
", cardApiTicket='" + cardApiTicket + '\'' +
|
||||||
|
", cardApiTicketExpiresTime='" + cardApiTicketExpiresTime + '\'' +
|
||||||
", tmpDirFile='" + tmpDirFile + '\'' +
|
", tmpDirFile='" + tmpDirFile + '\'' +
|
||||||
'}';
|
'}';
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package me.chanjar.weixin.mp.api;
|
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.WxMenu;
|
||||||
import me.chanjar.weixin.common.bean.WxJsapiSignature;
|
import me.chanjar.weixin.common.bean.WxJsapiSignature;
|
||||||
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult;
|
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult;
|
||||||
@ -770,7 +771,7 @@ public interface WxMpService {
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public boolean checkJSSDKCallbackDataSignature(Map<String, String> kvm, String signature);
|
public boolean checkJSSDKCallbackDataSignature(Map<String, String> kvm, String signature);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 发送微信红包给个人用户
|
* 发送微信红包给个人用户
|
||||||
* @param parameters
|
* @param parameters
|
||||||
@ -778,4 +779,81 @@ public interface WxMpService {
|
|||||||
* @throws WxErrorException
|
* @throws WxErrorException
|
||||||
*/
|
*/
|
||||||
public WxRedpackResult sendRedpack(Map<String, String> parameters) throws WxErrorException;
|
public WxRedpackResult sendRedpack(Map<String, String> parameters) throws WxErrorException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得卡券api_ticket,不强制刷新卡券api_ticket
|
||||||
|
* @see #getCardApiTicket(boolean)
|
||||||
|
* @return 卡券api_ticket
|
||||||
|
* @throws WxErrorException
|
||||||
|
*/
|
||||||
|
public String getCardApiTicket() throws WxErrorException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <pre>
|
||||||
|
* 获得卡券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
|
||||||
|
* </pre>
|
||||||
|
* @param forceRefresh 强制刷新
|
||||||
|
* @return 卡券api_ticket
|
||||||
|
* @throws WxErrorException
|
||||||
|
*/
|
||||||
|
public String getCardApiTicket(boolean forceRefresh) throws WxErrorException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <pre>
|
||||||
|
* 创建调用卡券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
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param optionalSignParam 参与签名的参数数组。
|
||||||
|
* 可以为下列字段:app_id, card_id, card_type, code, openid, location_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 单张卡券的唯一标准
|
||||||
|
* @return
|
||||||
|
* @throws WxErrorException
|
||||||
|
*/
|
||||||
|
public void consumeCardCode(String code) 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;
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import java.io.IOException;
|
|||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.StringReader;
|
import java.io.StringReader;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -14,8 +15,10 @@ import java.util.SortedMap;
|
|||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import com.google.gson.JsonPrimitive;
|
||||||
import me.chanjar.weixin.common.api.WxConsts;
|
import me.chanjar.weixin.common.api.WxConsts;
|
||||||
import me.chanjar.weixin.common.bean.WxAccessToken;
|
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.WxJsapiSignature;
|
||||||
import me.chanjar.weixin.common.bean.WxMenu;
|
import me.chanjar.weixin.common.bean.WxMenu;
|
||||||
import me.chanjar.weixin.common.bean.result.WxError;
|
import me.chanjar.weixin.common.bean.result.WxError;
|
||||||
@ -43,6 +46,7 @@ import me.chanjar.weixin.mp.bean.WxMpMaterialArticleUpdate;
|
|||||||
import me.chanjar.weixin.mp.bean.WxMpMaterialNews;
|
import me.chanjar.weixin.mp.bean.WxMpMaterialNews;
|
||||||
import me.chanjar.weixin.mp.bean.WxMpSemanticQuery;
|
import me.chanjar.weixin.mp.bean.WxMpSemanticQuery;
|
||||||
import me.chanjar.weixin.mp.bean.WxMpTemplateMessage;
|
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.WxMpMassSendResult;
|
||||||
import me.chanjar.weixin.mp.bean.result.WxMpMassUploadResult;
|
import me.chanjar.weixin.mp.bean.result.WxMpMassUploadResult;
|
||||||
import me.chanjar.weixin.mp.bean.result.WxMpMaterialCountResult;
|
import me.chanjar.weixin.mp.bean.result.WxMpMaterialCountResult;
|
||||||
@ -112,6 +116,11 @@ public class WxMpServiceImpl implements WxMpService {
|
|||||||
*/
|
*/
|
||||||
protected final Object globalJsapiTicketRefreshLock = new Object();
|
protected final Object globalJsapiTicketRefreshLock = new Object();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 全局的是否正在刷新卡券api_ticket的锁
|
||||||
|
*/
|
||||||
|
protected final Object globalCardApiTicketRefreshLock = new Object();
|
||||||
|
|
||||||
protected WxMpConfigStorage wxMpConfigStorage;
|
protected WxMpConfigStorage wxMpConfigStorage;
|
||||||
|
|
||||||
protected CloseableHttpClient httpClient;
|
protected CloseableHttpClient httpClient;
|
||||||
@ -1006,4 +1015,172 @@ public class WxMpServiceImpl implements WxMpService {
|
|||||||
throw new WxErrorException(error);
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <pre>
|
||||||
|
* 获得卡券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
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @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();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <pre>
|
||||||
|
* 创建调用卡券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
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param optionalSignParam 参与签名的参数数组。
|
||||||
|
* 可以为下列字段:app_id, card_id, card_type, code, openid, location_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<WxMpCardResult>() {
|
||||||
|
}.getType());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 卡券Code核销。核销失败会抛出异常
|
||||||
|
*
|
||||||
|
* @param code 单张卡券的唯一标准
|
||||||
|
* @throws WxErrorException
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void consumeCardCode(String code) throws WxErrorException {
|
||||||
|
String url = "https://api.weixin.qq.com/card/code/consume";
|
||||||
|
JsonObject param = new JsonObject();
|
||||||
|
param.addProperty("code", code);
|
||||||
|
post(url, param.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 卡券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<WxMpCardResult>() { }.getType());
|
||||||
|
if (!cardResult.getErrorCode().equals("0")) {
|
||||||
|
log.warn("朋友的券mark失败:{}", cardResult.getErrorMsg());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
@ -150,6 +150,31 @@ public class WxMpXmlMessage implements Serializable {
|
|||||||
@XStreamAlias("ErrorCount")
|
@XStreamAlias("ErrorCount")
|
||||||
private Integer 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")
|
@XStreamAlias("ScanCodeInfo")
|
||||||
private ScanCodeInfo scanCodeInfo = new ScanCodeInfo();
|
private ScanCodeInfo scanCodeInfo = new ScanCodeInfo();
|
||||||
|
|
||||||
@ -456,6 +481,54 @@ public class WxMpXmlMessage implements Serializable {
|
|||||||
this.errorCount = errorCount;
|
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() {
|
public WxMpXmlMessage.ScanCodeInfo getScanCodeInfo() {
|
||||||
return scanCodeInfo;
|
return scanCodeInfo;
|
||||||
}
|
}
|
||||||
@ -652,6 +725,11 @@ public class WxMpXmlMessage implements Serializable {
|
|||||||
", filterCount=" + filterCount +
|
", filterCount=" + filterCount +
|
||||||
", sentCount=" + sentCount +
|
", sentCount=" + sentCount +
|
||||||
", errorCount=" + errorCount +
|
", errorCount=" + errorCount +
|
||||||
|
", cardId='" + cardId + '\'' +
|
||||||
|
", isGiveByFriend=" + isGiveByFriend +
|
||||||
|
", userCardCode='" + userCardCode + '\'' +
|
||||||
|
", oldUserCardCode='" + oldUserCardCode + '\'' +
|
||||||
|
", outerId=" + outerId +
|
||||||
", scanCodeInfo=" + scanCodeInfo +
|
", scanCodeInfo=" + scanCodeInfo +
|
||||||
", sendPicsInfo=" + sendPicsInfo +
|
", sendPicsInfo=" + sendPicsInfo +
|
||||||
", sendLocationInfo=" + sendLocationInfo +
|
", sendLocationInfo=" + sendLocationInfo +
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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<WxMpCard> {
|
||||||
|
|
||||||
|
@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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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<WxMpCardResult> {
|
||||||
|
@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<WxMpCard>() {
|
||||||
|
}.getType());
|
||||||
|
|
||||||
|
cardResult.setCard(card);
|
||||||
|
|
||||||
|
return cardResult;
|
||||||
|
}
|
||||||
|
}
|
@ -38,6 +38,8 @@ public class WxMpGsonBuilder {
|
|||||||
INSTANCE.registerTypeAdapter(WxMpMaterialNewsBatchGetResult.WxMaterialNewsBatchGetNewsItem.class, new WxMpMaterialNewsBatchGetGsonItemAdapter());
|
INSTANCE.registerTypeAdapter(WxMpMaterialNewsBatchGetResult.WxMaterialNewsBatchGetNewsItem.class, new WxMpMaterialNewsBatchGetGsonItemAdapter());
|
||||||
INSTANCE.registerTypeAdapter(WxMpMaterialFileBatchGetResult.class, new WxMpMaterialFileBatchGetGsonAdapter());
|
INSTANCE.registerTypeAdapter(WxMpMaterialFileBatchGetResult.class, new WxMpMaterialFileBatchGetGsonAdapter());
|
||||||
INSTANCE.registerTypeAdapter(WxMpMaterialFileBatchGetResult.WxMaterialFileBatchGetNewsItem.class, new WxMpMaterialFileBatchGetGsonItemAdapter());
|
INSTANCE.registerTypeAdapter(WxMpMaterialFileBatchGetResult.WxMaterialFileBatchGetNewsItem.class, new WxMpMaterialFileBatchGetGsonItemAdapter());
|
||||||
|
INSTANCE.registerTypeAdapter(WxMpCardResult.class, new WxMpCardResultGsonAdapter());
|
||||||
|
INSTANCE.registerTypeAdapter(WxMpCard.class, new WxMpCardGsonAdapter());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Gson create() {
|
public static Gson create() {
|
||||||
|
Loading…
Reference in New Issue
Block a user