diff --git a/weixin4j-base/pom.xml b/weixin4j-base/pom.xml
index 48ca0837..6059f214 100644
--- a/weixin4j-base/pom.xml
+++ b/weixin4j-base/pom.xml
@@ -71,5 +71,10 @@
3.0.2
true
+
+ org.bouncycastle
+ bcprov-jdk16
+ 1.46
+
\ No newline at end of file
diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/api/MchApi.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/api/MchApi.java
index 6e07f29f..8f89afe8 100644
--- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/api/MchApi.java
+++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/api/MchApi.java
@@ -32,6 +32,8 @@ public class MchApi extends BaseApi {
private final static ResourceBundle WEIXIN_BUNDLE;
+ private final static String PEM_CERT_PREFIX = "-----BEGIN CERTIFICATE-----";
+
static {
WEIXIN_BUNDLE = ResourceBundle.getBundle("com/foxinmy/weixin4j/payment/weixin");
}
@@ -93,21 +95,28 @@ public class MchApi extends BaseApi {
*/
protected WeixinRequestExecutor getWeixinSSLExecutor() throws WeixinException {
if (weixinSSLExecutor == null) {
- try {
- InputStream is = null;
- File certificate = new File(
- Weixin4jConfigUtil.replaceClassPathValue(weixinAccount.getCertificateFile()));
- if (!certificate.exists() || !certificate.isFile()) {
- is = Weixin4jConfigUtil.CLASSLOADER.getResourceAsStream(certificate.getName());
- } else {
- is = new FileInputStream(certificate);
+ if(weixinAccount.getCertificateFile().startsWith(PEM_CERT_PREFIX)){
+ // 证书是PEM格式,直接使用PEM内容生成请求执行类
+ this.weixinSSLExecutor = weixinExecutor.createSSLRequestExecutor(weixinAccount.getMchId(),
+ weixinAccount.getCertificateFile(), weixinAccount.getCertificateKey());
+ }else {
+ // 证书是p12格式,读取证书文件并生成请求执行类
+ try {
+ InputStream is = null;
+ File certificate = new File(
+ Weixin4jConfigUtil.replaceClassPathValue(weixinAccount.getCertificateFile()));
+ if (!certificate.exists() || !certificate.isFile()) {
+ is = Weixin4jConfigUtil.CLASSLOADER.getResourceAsStream(certificate.getName());
+ } else {
+ is = new FileInputStream(certificate);
+ }
+ if (is == null) {
+ throw new WeixinException("Invalid certificate file : " + certificate.toString());
+ }
+ this.weixinSSLExecutor = weixinExecutor.createSSLRequestExecutor(weixinAccount.getCertificateKey(), is);
+ } catch (IOException e) {
+ throw new WeixinException("IO Error on createSSLRequestExecutor", e);
}
- if (is == null) {
- throw new WeixinException("Invalid certificate file : " + certificate.toString());
- }
- this.weixinSSLExecutor = weixinExecutor.createSSLRequestExecutor(weixinAccount.getCertificateKey(), is);
- } catch (IOException e) {
- throw new WeixinException("IO Error on createSSLRequestExecutor", e);
}
}
return this.weixinSSLExecutor;
diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/WeixinRequestExecutor.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/WeixinRequestExecutor.java
index 462f8faa..c058041d 100644
--- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/WeixinRequestExecutor.java
+++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/WeixinRequestExecutor.java
@@ -1,12 +1,22 @@
package com.foxinmy.weixin4j.http.weixin;
+import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.security.KeyStore;
+import java.security.*;
+import java.security.cert.CertificateFactory;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Arrays;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import javax.xml.bind.DatatypeConverter;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.http.HttpClient;
@@ -29,6 +39,8 @@ import com.foxinmy.weixin4j.logging.InternalLogLevel;
import com.foxinmy.weixin4j.logging.InternalLogger;
import com.foxinmy.weixin4j.logging.InternalLoggerFactory;
import com.foxinmy.weixin4j.util.Consts;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import static java.util.regex.Pattern.CASE_INSENSITIVE;
/**
* 负责微信请求的执行
@@ -56,6 +68,18 @@ public class WeixinRequestExecutor {
this.httpClient = HttpClientFactory.getInstance(params);
}
+ private static final Pattern CERT_PATTERN = Pattern.compile(
+ "-+BEGIN\\s+.*CERTIFICATE[^-]*-+(?:\\s|\\r|\\n)+" + // Header
+ "([a-z0-9+/=\\r\\n]+)" + // Base64 text
+ "-+END\\s+.*CERTIFICATE[^-]*-+", // Footer
+ CASE_INSENSITIVE);
+
+ private static final Pattern KEY_PATTERN = Pattern.compile(
+ "-+BEGIN\\s+.*PRIVATE\\s+KEY[^-]*-+(?:\\s|\\r|\\n)+" + // Header
+ "([a-z0-9+/=\\r\\n]+)" + // Base64 text
+ "-+END\\s+.*PRIVATE\\s+KEY[^-]*-+", // Footer
+ CASE_INSENSITIVE);
+
/**
* Post方法执行微信请求
*
@@ -250,4 +274,69 @@ public class WeixinRequestExecutor {
params.setSSLContext(sslContext);
return new WeixinRequestExecutor(params);
}
+
+ /**
+ * 使用PEM格式证书创建SSL微信请求对象
+ *
+ * @param pemCertificate
+ * PEM格式证书内容
+ * @param pemPrivateKey
+ * PEM格式证书私钥
+ * @return
+ */
+ public WeixinRequestExecutor createSSLRequestExecutor(String password, String pemCertificate, String pemPrivateKey) throws WeixinException{
+ Security.addProvider(new BouncyCastleProvider());
+
+ try {
+ byte[] certBytes = parseDERFromPEM(pemCertificate);
+ byte[] keyBytes = parseDERFromPEM(pemPrivateKey);
+
+ char[] passwordChars = password.toCharArray();
+ X509Certificate cert = generateCertificateFromDER(certBytes);
+ RSAPrivateKey key = generatePrivateKeyFromDER(keyBytes);
+
+ KeyStore keystore = KeyStore.getInstance("JKS");
+ keystore.load(null);
+ keystore.setCertificateEntry("cert-alias", cert);
+ keystore.setKeyEntry("key-alias", key, passwordChars, new X509Certificate[] {cert});
+
+ KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
+ kmf.init(keystore, passwordChars);
+
+ SSLContext context = SSLContext.getInstance("TLS");
+ context.init(kmf.getKeyManagers(), null, new java.security.SecureRandom());
+
+ return createSSLRequestExecutor(context);
+ } catch (Exception e) {
+ throw new WeixinException("Certificate load error", e);
+ }
+
+ }
+
+ private static byte[] parseDERFromPEM(String data) throws KeyStoreException {
+ Matcher matcher = CERT_PATTERN.matcher(data);
+ String content = "";
+ if(!matcher.find()){
+ matcher = KEY_PATTERN.matcher(data);
+ if(!matcher.find()){
+ throw new KeyStoreException("found no private key or certificate from content:"+ data);
+ }
+ }
+ content = matcher.group(1);
+ return DatatypeConverter.parseBase64Binary(content);
+ }
+
+ private static RSAPrivateKey generatePrivateKeyFromDER(byte[] keyBytes) throws InvalidKeySpecException, NoSuchAlgorithmException {
+ PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
+
+ KeyFactory factory = KeyFactory.getInstance("RSA");
+
+ return (RSAPrivateKey)factory.generatePrivate(spec);
+ }
+
+ protected static X509Certificate generateCertificateFromDER(byte[] certBytes) throws CertificateException {
+ CertificateFactory factory = CertificateFactory.getInstance("X.509");
+
+ return (X509Certificate)factory.generateCertificate(new ByteArrayInputStream(certBytes));
+ }
}
diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/model/card/CardCoupons.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/model/card/CardCoupons.java
index dab7f995..b22e93bc 100644
--- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/model/card/CardCoupons.java
+++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/model/card/CardCoupons.java
@@ -38,6 +38,30 @@ public final class CardCoupons {
public static MemberCard.Builder customMemberCard(){
return new MemberCard.Builder();
}
+
+ /**
+ * 礼品卡信息构造器
+ *
+ * @return
+ */
+ public static GiftCard.Builder customGiftCard() {
+ return new GiftCard.Builder();
+ }
+
+ /**
+ * 礼品卡货架主题信息构造器
+ *
+ * @return
+ */
+ public static PageTheme.Builder customCardPageTheme(){ return new PageTheme.Builder(); }
+
+ /**
+ * 礼品卡货架信息构造器
+ *
+ * @return
+ */
+ public static GiftCardPage.Builder customCardPage(){ return new GiftCardPage.Builder(); }
+
/**
* 创建代金券
*
@@ -122,4 +146,32 @@ public final class CardCoupons {
MemberCard memberCard = new MemberCard(baseBuilder.build(), memberCardBudiler);
return memberCard;
}
+
+ /**
+ * 创建单品类礼品卡
+ *
+ * @param baseBuilder
+ * 卡券基础信息构造器 必填
+ * @param giftCardBuilder
+ * 礼品卡自身参数构造器 必填
+ * @return
+ */
+ public static VoucherCard createVoucherCard(CouponBaseInfo.Builder baseBuilder, GiftCard.Builder giftCardBuilder){
+ baseBuilder.build();
+ VoucherCard voucherCard = new VoucherCard(baseBuilder.build(), giftCardBuilder);
+ return voucherCard;
+ }
+
+ /**
+ * 创建储值类礼品卡
+ *
+ * @param baseBuilder
+ * @param giftCardBuilder
+ * @return
+ */
+ public static GiftCard createGiftCard(CouponBaseInfo.Builder baseBuilder, GiftCard.Builder giftCardBuilder){
+ baseBuilder.build();
+ GiftCard giftCard = new GiftCard(baseBuilder.build(), giftCardBuilder);
+ return giftCard;
+ }
}
\ No newline at end of file
diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/model/card/CardInfo.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/model/card/CardInfo.java
new file mode 100644
index 00000000..757e4cc5
--- /dev/null
+++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/model/card/CardInfo.java
@@ -0,0 +1,183 @@
+package com.foxinmy.weixin4j.model.card;
+
+import com.alibaba.fastjson.annotation.JSONField;
+
+/**
+ * 已领取的礼品卡信息
+ * 用于查询用户的礼品卡信息时,作为参数或返回对象
+ *
+ * @author kit (kit@muses.cc)
+ * @date 2018-11-23
+ */
+public class CardInfo {
+ /**
+ * 礼品卡的code
+ */
+ private String code;
+ /**
+ * 礼品卡的card_id
+ */
+ @JSONField(name = "card_id")
+ private String cardId;
+ /**
+ * 生效时间
+ */
+ @JSONField(name = "begin_time")
+ private long beginTime;
+ /**
+ * 结束时间
+ */
+ @JSONField(name = "end_time")
+ private long endTime;
+ /**
+ * 当前的余额额度
+ */
+ private int balance;
+ /**
+ * 礼品卡卡面显示的卡号,若没设置则与code相同
+ */
+ @JSONField(name = "card_number")
+ private String cardNumber;
+ /**
+ * 用于支持商家激活时针对单个礼品卡分配自定义的礼品卡背景。
+ */
+ @JSONField(name = "background_pic_url")
+ private String backgroundPicUrl;
+ /**
+ * 自定义金额消耗记录,不超过14个汉字。
+ */
+ @JSONField(name = "record_balance")
+ private String recordBalance;
+ /**
+ * 创建时字段custom_field1定义类型的最新数值,限制为4个汉字,12字节。
+ */
+ @JSONField(name = "custom_field_value1")
+ private String customFieldValue1;
+ /**
+ * 创建时字段custom_field2定义类型的最新数值,限制为4个汉字,12字节。
+ */
+ @JSONField(name = "custom_field_value2")
+ private String customFieldValue2;
+ /**
+ * 创建时字段custom_field3定义类型的最新数值,限制为4个汉字,12字节。
+ */
+ @JSONField(name = "custom_field_value3")
+ private String customFieldValue3;
+ /**
+ * 控制本次积分变动后转赠入口是否出现
+ */
+ @JSONField(name = "can_give_friend")
+ private Boolean canGiveFriend;
+ /**
+ * 该卡的价格
+ */
+ private int price;
+ /**
+ * 祝福语
+ */
+ @JSONField(name = "default_gifting_msg")
+ private String defaultGiftingMsg;
+
+ public String getCode() {
+ return code;
+ }
+
+ public void setCode(String code) {
+ this.code = code;
+ }
+
+ 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 int getBalance() {
+ return balance;
+ }
+
+ public void setBalance(int balance) {
+ this.balance = balance;
+ }
+
+ public String getCardNumber() {
+ return cardNumber;
+ }
+
+ public void setCardNumber(String cardNumber) {
+ this.cardNumber = cardNumber;
+ }
+
+ public String getBackgroundPicUrl() {
+ return backgroundPicUrl;
+ }
+
+ public void setBackgroundPicUrl(String backgroundPicUrl) {
+ this.backgroundPicUrl = backgroundPicUrl;
+ }
+
+ public String getRecordBalance() {
+ return recordBalance;
+ }
+
+ public void setRecordBalance(String recordBalance) {
+ this.recordBalance = recordBalance;
+ }
+
+ public String getCustomFieldValue1() {
+ return customFieldValue1;
+ }
+
+ public void setCustomFieldValue1(String customFieldValue1) {
+ this.customFieldValue1 = customFieldValue1;
+ }
+
+ public String getCustomFieldValue2() {
+ return customFieldValue2;
+ }
+
+ public void setCustomFieldValue2(String customFieldValue2) {
+ this.customFieldValue2 = customFieldValue2;
+ }
+
+ public String getCustomFieldValue3() {
+ return customFieldValue3;
+ }
+
+ public void setCustomFieldValue3(String customFieldValue3) {
+ this.customFieldValue3 = customFieldValue3;
+ }
+
+ public Boolean getCanGiveFriend() {
+ return canGiveFriend;
+ }
+
+ public void setCanGiveFriend(Boolean canGiveFriend) {
+ this.canGiveFriend = canGiveFriend;
+ }
+
+ public void CardItem(){}
+
+ public void CardItem(String code, String cardId){
+ this.code = code;
+ this.cardId = cardId;
+ }
+}
diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/model/card/CardItem.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/model/card/CardItem.java
new file mode 100644
index 00000000..ecb99ea7
--- /dev/null
+++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/model/card/CardItem.java
@@ -0,0 +1,77 @@
+package com.foxinmy.weixin4j.model.card;
+
+import com.alibaba.fastjson.annotation.JSONField;
+
+/**
+ * 礼品卡货架主题内的礼品卡描述项
+ *
+ * @author kit(kit.li@qq.com)
+ * @date 2018年10月30日
+ */
+public class CardItem {
+ /**
+ * 待上架的card_id
+ */
+ @JSONField(name = "card_id")
+ private String cardId;
+ /**
+ * 商品名,不填写默认为卡名称
+ */
+ private String title;
+ /**
+ * 商品缩略图,1000像素*600像素以下
+ */
+ @JSONField(name = "pic_url")
+ private String picUrl;
+ /**
+ * 商品简介
+ */
+ private String desc;
+
+ public CardItem(){
+
+ }
+
+ public CardItem(String cardId) {
+ this.cardId = cardId;
+ }
+
+ public CardItem(String cardId, String title, String picUrl, String desc) {
+ this.cardId = cardId;
+ this.title = title;
+ this.picUrl = picUrl;
+ this.desc = desc;
+ }
+
+ public String getCardId() {
+ return cardId;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public String getPicUrl() {
+ return picUrl;
+ }
+
+ public String getDesc() {
+ return desc;
+ }
+
+ public void setCardId(String cardId) {
+ this.cardId = cardId;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public void setPicUrl(String picUrl) {
+ this.picUrl = picUrl;
+ }
+
+ public void setDesc(String desc) {
+ this.desc = desc;
+ }
+}
diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/model/card/CouponBaseInfo.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/model/card/CouponBaseInfo.java
index 8b4bf1f4..1aec6d7f 100644
--- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/model/card/CouponBaseInfo.java
+++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/model/card/CouponBaseInfo.java
@@ -21,7 +21,18 @@ import com.foxinmy.weixin4j.type.card.CardColor;
*/
public class CouponBaseInfo implements Serializable {
- private static final long serialVersionUID = -5725424121330101716L;
+ private static final long serialVersionUID = -5725424121330101717L;
+
+ /**
+ * 礼品卡信息,目前只有一个价格属性,礼品卡时必填
+ */
+ @JSONField(name = "giftcard_info")
+ private JSONObject giftcardInfo;
+ /**
+ * 礼品卡最大可赠送次数,必填
+ */
+ @JSONField(name = "max_give_friend_times")
+ private int maxGiveFriendTimes;
/**
* 卡券的商户logo,建议像素为300*300。
*/
@@ -71,7 +82,7 @@ public class CouponBaseInfo implements Serializable {
@JSONField(name = "use_custom_code")
private Boolean useCustomCode;
/**
- * 指定特殊用户群体
+ * 指定特殊用户群体,礼品卡建议填写false
*/
@JSONField(name = "bind_openid")
private Boolean bindOpenId;
@@ -140,12 +151,12 @@ public class CouponBaseInfo implements Serializable {
*/
private String source;
/**
- * 每人可领券的数量限制,不填写默认为50。
+ * 每人可领券的数量限制,不填写默认为50。礼品卡时须填0,不限制
*/
@JSONField(name = "get_limit")
private int limitNum;
/**
- * 卡券领取页面是否可分享
+ * 卡券领取页面是否可分享,礼品卡建议填写false
*/
@JSONField(name = "can_share")
private boolean canShare;
@@ -157,9 +168,41 @@ public class CouponBaseInfo implements Serializable {
@JSONField(name = "need_push_on_view")
private Boolean needPushOnView;
+ /**
+ * 礼品卡用属性,自定义cell对应的小程序username,格式为公众号原始id@app。如:gh_86a091e50ad4@app 选填
+ */
+ @JSONField(name = "center_app_brand_user_name")
+ private String centerAppBrandUserName;
+ /**
+ * 礼品卡用属性,自定义cell对应的小程序路径. 如:pages/index/index 选填
+ */
+ @JSONField(name = "center_app_brand_pass")
+ private String centerAppBrandPass;
+ /**
+ * 礼品卡用属性,自定义cell对应的小程序username,格式为公众号原始id@app 选填
+ */
+ @JSONField(name = "custom_app_brand_user_name")
+ private String customAppBrandUserName;
+ /**
+ * 礼品卡用属性,自定义cell对应的小程序路径。选填
+ */
+ @JSONField(name = "custom_app_brand_pass")
+ private String customAppBrandPass;
+ /**
+ * 礼品卡用属性,自定义cell对应的小程序username,格式为公众号原始id@app 选填
+ */
+ @JSONField(name = "promotion_app_brand_user_name")
+ private String promotionAppBrandUserName;
+ /**
+ * 礼品卡用属性,自定义cell对应的小程序路径
+ */
+ @JSONField(name = "promotion_app_brand_pass")
+ private String promotionAppBrandPass;
private CouponBaseInfo(Builder builder) {
+ this.giftcardInfo = builder.giftcardInfo;
this.logoUrl = builder.logoUrl;
+ this.maxGiveFriendTimes = builder.maxGiveFriendTimes;
this.brandName = builder.brandName;
this.title = builder.title;
this.codeType = builder.codeType;
@@ -176,12 +219,18 @@ public class CouponBaseInfo implements Serializable {
this.centerTitle = builder.centerTitle;
this.centerUrl = builder.centerUrl;
this.centerSubTitle = builder.centerSubTitle;
+ this.centerAppBrandUserName = builder.centerAppBrandUserName;
+ this.centerAppBrandPass = builder.centerAppBrandPass;
this.customTitle = builder.customTitle;
this.customUrl = builder.customUrl;
this.customSubTitle = builder.customSubTitle;
+ this.customAppBrandUserName = builder.customAppBrandUserName;
+ this.customAppBrandPass = builder.customAppBrandPass;
this.promotionTitle = builder.promotionTitle;
this.promotionUrl = builder.promotionUrl;
this.promotionSubTitle = builder.promotionSubTitle;
+ this.promotionAppBrandUserName = builder.promotionAppBrandUserName;
+ this.promotionAppBrandPass = builder.promotionAppBrandPass;
this.source = builder.source;
this.limitNum = builder.limitNum;
this.canShare = builder.canShare;
@@ -301,23 +350,86 @@ public class CouponBaseInfo implements Serializable {
return needPushOnView;
}
+ public JSONObject getGiftcardInfo() {
+ return giftcardInfo;
+ }
+
+ public int getMaxGiveFriendTimes() {
+ return maxGiveFriendTimes;
+ }
+
+ public Boolean getUseCustomCode() {
+ return useCustomCode;
+ }
+
+ public Boolean getBindOpenId() {
+ return bindOpenId;
+ }
+
+ public String getCenterAppBrandUserName() {
+ return centerAppBrandUserName;
+ }
+
+ public String getCenterAppBrandPass() {
+ return centerAppBrandPass;
+ }
+
+ public String getCustomAppBrandUserName() {
+ return customAppBrandUserName;
+ }
+
+ public String getCustomAppBrandPass() {
+ return customAppBrandPass;
+ }
+
+ public String getPromotionAppBrandUserName() {
+ return promotionAppBrandUserName;
+ }
+
+ public String getPromotionAppBrandPass() {
+ return promotionAppBrandPass;
+ }
+
@Override
public String toString() {
- return "logoUrl=" + logoUrl + ", brandName=" + brandName + ", title="
- + title + ", codeType=" + codeType + ", cardColor=" + cardColor
- + ", notice=" + notice + ", description=" + description
- + ", sku=" + sku + ", date=" + date + ", useCustomCode="
- + useCustomCode + ", bindOpenId=" + bindOpenId
- + ", servicePhone=" + servicePhone + ", locationIds="
- + locationIds + ", useAllLocation=" + useAllLocation
- + ", centerTitle=" + centerTitle + ", centerUrl=" + centerUrl
- + ", centerSubTitle=" + centerSubTitle + ", customTitle="
- + customTitle + ", customUrl=" + customUrl
- + ", customSubTitle=" + customSubTitle + ", promotionTitle="
- + promotionTitle + ", promotionUrl=" + promotionUrl
- + ", promotionSubTitle=" + promotionSubTitle + ", source="
- + source + ", limitNum=" + limitNum + ", canShare=" + canShare
- + ", canGiveFriend=" + canGiveFriend;
+ return "CouponBaseInfo{" +
+ "giftcardInfo=" + giftcardInfo +
+ ", maxGiveFriendTimes=" + maxGiveFriendTimes +
+ ", logoUrl='" + logoUrl + '\'' +
+ ", brandName='" + brandName + '\'' +
+ ", title='" + title + '\'' +
+ ", codeType=" + codeType +
+ ", cardColor=" + cardColor +
+ ", notice='" + notice + '\'' +
+ ", description='" + description + '\'' +
+ ", sku=" + sku +
+ ", date=" + date +
+ ", useCustomCode=" + useCustomCode +
+ ", bindOpenId=" + bindOpenId +
+ ", servicePhone='" + servicePhone + '\'' +
+ ", locationIds=" + locationIds +
+ ", useAllLocation=" + useAllLocation +
+ ", centerTitle='" + centerTitle + '\'' +
+ ", centerUrl='" + centerUrl + '\'' +
+ ", centerSubTitle='" + centerSubTitle + '\'' +
+ ", customTitle='" + customTitle + '\'' +
+ ", customUrl='" + customUrl + '\'' +
+ ", customSubTitle='" + customSubTitle + '\'' +
+ ", promotionTitle='" + promotionTitle + '\'' +
+ ", promotionUrl='" + promotionUrl + '\'' +
+ ", promotionSubTitle='" + promotionSubTitle + '\'' +
+ ", source='" + source + '\'' +
+ ", limitNum=" + limitNum +
+ ", canShare=" + canShare +
+ ", canGiveFriend=" + canGiveFriend +
+ ", needPushOnView=" + needPushOnView +
+ ", centerAppBrandUserName='" + centerAppBrandUserName + '\'' +
+ ", centerAppBrandPass='" + centerAppBrandPass + '\'' +
+ ", customAppBrandUserName='" + customAppBrandUserName + '\'' +
+ ", customAppBrandPass='" + customAppBrandPass + '\'' +
+ ", promotionAppBrandUserName='" + promotionAppBrandUserName + '\'' +
+ ", promotionAppBrandPass='" + promotionAppBrandPass + '\'' +
+ '}';
}
public void cleanCantUpdateField() {
@@ -337,10 +449,18 @@ public class CouponBaseInfo implements Serializable {
* @since JDK 1.6
*/
public static final class Builder {
+ /**
+ * 礼品卡信息,目前只有一个礼品卡的价格属性,必填
+ */
+ private JSONObject giftcardInfo;
/**
* 卡券的商户logo,建议像素为300*300
*/
private String logoUrl;
+ /**
+ * 礼品卡最大可赠送次数,必填
+ */
+ private int maxGiveFriendTimes;
/**
* 商户名字,字数上限为12个汉字,如:海底捞
*/
@@ -452,11 +572,37 @@ public class CouponBaseInfo implements Serializable {
* 用户点击进入卡券时推送事件
*/
private boolean needPushOnView;
+ /**
+ * 自定义cell对应的小程序username,格式为公众号原始id@app。如:gh_86a091e50ad4@app 选填
+ */
+ private String centerAppBrandUserName;
+ /**
+ * 自定义cell对应的小程序路径. 如:pages/index/index 选填
+ */
+ private String centerAppBrandPass;
+ /**
+ * 自定义cell对应的小程序username,格式为公众号原始id@app 选填
+ */
+ private String customAppBrandUserName;
+ /**
+ * 自定义cell对应的小程序路径。选填
+ */
+ private String customAppBrandPass;
+ /**
+ * 自定义cell对应的小程序username,格式为公众号原始id@app
+ */
+ private String promotionAppBrandUserName;
+ /**
+ * 自定义cell对应的小程序路径
+ */
+ private String promotionAppBrandPass;
/**
* 默认永久有效
*/
public Builder() {
+ this.giftcardInfo = new JSONObject();
+ this.maxGiveFriendTimes = 1;
this.sku = new JSONObject();
this.date = new JSONObject();
this.date.put("type",CardActiveType.DATE_TYPE_PERMANENT);
@@ -466,6 +612,46 @@ public class CouponBaseInfo implements Serializable {
this.limitNum = 50;
}
+ public Builder customAppBrandPass(String pass){
+ this.customAppBrandPass = pass;
+ return this;
+ }
+
+ public Builder centerAppBrandUserName(String name){
+ this.centerAppBrandUserName = name;
+ return this;
+ }
+
+ public Builder centerAppBrandPass(String pass){
+ this.centerAppBrandPass = pass;
+ return this;
+ }
+
+ public Builder customAppBrandUserName(String name){
+ this.customAppBrandUserName = name;
+ return this;
+ }
+
+ public Builder promotionAppBrandUserName(String userName){
+ this.promotionAppBrandUserName = userName;
+ return this;
+ }
+
+ public Builder promotionAppBrandPass(String pass){
+ this.promotionAppBrandPass = pass;
+ return this;
+ }
+
+ public Builder maxGiveFriendTimes(int times){
+ this.maxGiveFriendTimes = times;
+ return this;
+ }
+
+ public Builder price(int price){
+ this.giftcardInfo.put("price", price);
+ return this;
+ }
+
/**
* 设置商户logo
*
diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/model/card/GiftCard.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/model/card/GiftCard.java
new file mode 100644
index 00000000..a62d69f5
--- /dev/null
+++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/model/card/GiftCard.java
@@ -0,0 +1,219 @@
+package com.foxinmy.weixin4j.model.card;
+
+import com.alibaba.fastjson.annotation.JSONField;
+import com.foxinmy.weixin4j.type.card.CardType;
+import com.foxinmy.weixin4j.type.card.SubCardType;
+
+/**
+ * 储值类礼品卡
+ *
+ * @author kit(kit.li@qq.com)
+ * @date 2018年10月26日
+ */
+public class GiftCard extends CardCoupon {
+
+ /**
+ * 子卡券类型
+ */
+ @JSONField(name = "sub_card_type")
+ private final String subCardType;
+ /**
+ * 礼品卡背景图片,非必需
+ */
+ @JSONField(name = "background_pic_url")
+ private String backgroundPicUrl;
+ /**
+ * 是否支持积分,填写true或者false。默认为false
+ */
+ @JSONField(name = "supply_bonus")
+ private boolean supplyBonus;
+ /**
+ * 是否支持余额,填写true或者false。默认为false
+ */
+ @JSONField(name = "supply_balance")
+ private boolean supplyBalance;
+ /**
+ * 自定义会员信息类目,会员卡激活后显示,包含name和url字段
+ */
+ @JSONField(name = "custom_field1")
+ private MemCardCustomField customField1;
+ /**
+ * 自定义会员信息类目,会员卡激活后显示,包含name和url字段
+ */
+ @JSONField(name = "custom_field2")
+ private MemCardCustomField customField2;
+ /**
+ * 自定义会员信息类目,会员卡激活后显示,包含name和url字段
+ */
+ @JSONField(name = "custom_field3")
+ private MemCardCustomField customField3;
+ /**
+ * 是否自动激活,若开发者不需要额外激活流程则填写true。
+ */
+ @JSONField(name = "auto_activate")
+ private boolean autoActivate;
+ /**
+ * 初始余额,用户购买礼品卡后卡面上显示的初始余额
+ */
+ @JSONField(name = "init_balance")
+ private Integer initBalance;
+
+ public GiftCard(CouponBaseInfo baseInfo, GiftCard.Builder builder){
+ super(baseInfo);
+ this.subCardType = SubCardType.VOUCHER.name();
+ this.autoActivate = builder.isAutoActivate();
+ this.backgroundPicUrl = builder.getBackgroundPicUrl();
+ this.customField1 = builder.getCustomField1();
+ this.customField2 = builder.getCustomField2();
+ this.customField3 = builder.getCustomField3();
+ this.supplyBalance = builder.isSupplyBalance();
+ this.supplyBonus = builder.isSupplyBonus();
+ this.initBalance = builder.getInitBalance();
+ }
+
+ @JSONField(serialize = false)
+ @Override
+ public CardType getCardType() {
+ return CardType.GENERAL_COUPON;
+ }
+
+ public static final class Builder {
+ private String backgroundPicUrl;
+ private boolean supplyBonus;
+ private boolean supplyBalance;
+ private boolean autoActivate;
+ private Integer initBalance;
+ private MemCardCustomField customField1;
+ private MemCardCustomField customField2;
+ private MemCardCustomField customField3;
+
+ public Builder(){
+ this.autoActivate = true;
+ this.supplyBalance = false;
+ this.supplyBonus = false;
+ }
+
+ public String getBackgroundPicUrl() {
+ return backgroundPicUrl;
+ }
+
+ public boolean isSupplyBonus() {
+ return supplyBonus;
+ }
+
+ public boolean isSupplyBalance() {
+ return supplyBalance;
+ }
+
+ public boolean isAutoActivate() {
+ return autoActivate;
+ }
+
+ public Integer getInitBalance() {
+ return initBalance;
+ }
+
+ public MemCardCustomField getCustomField1() {
+ return customField1;
+ }
+
+ public MemCardCustomField getCustomField2() {
+ return customField2;
+ }
+
+ public MemCardCustomField getCustomField3() {
+ return customField3;
+ }
+
+ /**
+ * 设置礼品卡背景图片
+ * @param url
+ * @return
+ */
+ public Builder backgroundPicUrl(String url){
+ this.backgroundPicUrl = url;
+ return this;
+ }
+
+ /**
+ * 设置是否支持积分(目前未清楚单品类礼品卡是否支持积分)
+ * @param supplyBonus
+ * @return
+ */
+ public Builder supplyBonus(boolean supplyBonus){
+ this.supplyBonus = supplyBonus;
+ return this;
+ }
+
+ /**
+ * 设置是否支持余额(需礼品卡类型为GIFT_CARD,单品类礼品卡VOUCHER并非用于储值)
+ * 注意事项:开发者仅能在supply_balance和custom_field1、custom_field2、custom_field3中选择最多3个填写,否则报错。
+ * @param supplyBalance
+ * @return
+ */
+ public Builder supplyBalance(boolean supplyBalance){
+ this.supplyBalance = supplyBalance;
+ return this;
+ }
+
+ /**
+ * 设置礼品卡是否自动激活,若开发者不需要额外激活流程则填写true。
+ * @param autoActivate
+ * @return
+ */
+ public Builder autoActivate(boolean autoActivate){
+ this.autoActivate = autoActivate;
+ return this;
+ }
+
+ /**
+ * 设置初始化的余额(需礼品卡类型为GIFT_CARD,单品类礼品卡VOUCHER并非用于储值)
+ * @param initBalance
+ * @return
+ */
+ public Builder initBalance(Integer initBalance){
+ this.initBalance = initBalance;
+ return this;
+ }
+
+ /**
+ * 设置自定义会员信息类目
+ * 注意事项:开发者仅能在supply_balance和custom_field1、custom_field2、custom_field3中选择最多3个填写,否则报错。
+ * @param name 自定义信息类目名称
+ * @param url 自定义信息类目跳转url
+ * @return
+ */
+ public Builder customField1(String name, String url){
+ MemCardCustomField field = new MemCardCustomField(name, url, null);
+ this.customField1 = field;
+ return this;
+ }
+
+ /**
+ * 设置自定义会员信息类目
+ * 注意事项:开发者仅能在supply_balance和custom_field1、custom_field2、custom_field3中选择最多3个填写,否则报错。
+ * @param name 自定义信息类目名称
+ * @param url 自定义信息类目跳转url
+ * @return
+ */
+ public Builder customField2(String name, String url){
+ MemCardCustomField field = new MemCardCustomField(name, url, null);
+ this.customField2 = field;
+ return this;
+ }
+
+ /**
+ * 设置自定义会员信息类目
+ * 注意事项:开发者仅能在supply_balance和custom_field1、custom_field2、custom_field3中选择最多3个填写,否则报错。
+ * @param name 自定义信息类目名称
+ * @param url 自定义信息类目跳转url
+ * @return
+ */
+ public Builder customField3(String name, String url){
+ MemCardCustomField field = new MemCardCustomField(name, url, null);
+ this.customField3 = field;
+ return this;
+ }
+ }
+
+}
diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/model/card/GiftCardOrder.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/model/card/GiftCardOrder.java
new file mode 100644
index 00000000..c96d77c8
--- /dev/null
+++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/model/card/GiftCardOrder.java
@@ -0,0 +1,11 @@
+package com.foxinmy.weixin4j.model.card;
+
+/**
+ * 礼品卡销售订单信息
+ *
+ * @author kit (kit.li@qq.com)
+ */
+public class GiftCardOrder {
+ private String orderId;
+ private String pageId;
+}
diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/model/card/GiftCardPage.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/model/card/GiftCardPage.java
new file mode 100644
index 00000000..e993a0b9
--- /dev/null
+++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/model/card/GiftCardPage.java
@@ -0,0 +1,351 @@
+package com.foxinmy.weixin4j.model.card;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.alibaba.fastjson.annotation.JSONField;
+
+/**
+ * 礼品卡货架
+ *
+ * @author kit(kit.li@qq.com)
+ * @date 2018年10月30日
+ */
+public class GiftCardPage {
+ @JSONField(name = "page_id")
+ private String pageId;
+ /**
+ * 礼品卡货架名称
+ */
+ @JSONField(name = "page_title")
+ private String title;
+ /**
+ * 是否支持一次购买多张及发送至群,填true或者false,若填true则支持,默认为false
+ */
+ @JSONField(name = "support_multi")
+ private Boolean supportMulti;
+ /**
+ * 礼品卡货架是否支持买给自己,填true或者false,若填true则支持,默认为false
+ */
+ @JSONField(name = "support_buy_for_self")
+ private Boolean supportBuyForSelf;
+ /**
+ * 礼品卡货架主题页顶部banner图片,须先将图片上传至CDN,建议尺寸为750px*630px
+ */
+ @JSONField(name = "banner_pic_url")
+ private String bannerPicUrl;
+ /**
+ * 主题结构体
+ */
+ @JSONField(name = "theme_list")
+ private JSONArray themeList;
+ /**
+ * 主题分类列表
+ */
+ @JSONField(name = "category_list")
+ private JSONArray categoryList;
+ /**
+ * 商家地址
+ */
+ private String address;
+ /**
+ * 商家服务电话
+ */
+ @JSONField(name = "service_phone")
+ private String servicePhone;
+ /**
+ * 商家使用说明,用于描述退款、发票等流程
+ */
+ @JSONField(name = "biz_description")
+ private String description;
+ /**
+ * 该货架的订单是否支持开发票,填true或者false,若填true则需要使用API设置支付后开票功能,默认为false
+ */
+ @JSONField(name = "need_receipt")
+ private Boolean needReceipt;
+ /**
+ * 商家自定义链接,用于承载退款、发票等流程
+ */
+ @JSONField(name = "cell_1")
+ private JSONObject cell1;
+ /**
+ * 商家自定义链接,用于承载退款、发票等流程
+ */
+ @JSONField(name = "cell_2")
+ private JSONObject cell2;
+
+ public String getPageId() { return pageId; }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public Boolean getSupportMulti() {
+ return supportMulti;
+ }
+
+ public Boolean getSupportBuyForSelf() {
+ return supportBuyForSelf;
+ }
+
+ public String getBannerPicUrl() {
+ return bannerPicUrl;
+ }
+
+ public JSONArray getThemeList() {
+ return themeList;
+ }
+
+ public JSONArray getCategoryList() {
+ return categoryList;
+ }
+
+ public String getAddress() {
+ return address;
+ }
+
+ public String getServicePhone() {
+ return servicePhone;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public Boolean getNeedReceipt() {
+ return needReceipt;
+ }
+
+ public JSONObject getCell1() {
+ return cell1;
+ }
+
+ public JSONObject getCell2() {
+ return cell2;
+ }
+
+ public GiftCardPage(Builder builder){
+ this.pageId = builder.pageId;
+ this.address = builder.address;
+ this.bannerPicUrl = builder.bannerPicUrl;
+ this.categoryList = builder.categoryList;
+ this.cell1 = builder.cell1;
+ this.cell2 = builder.cell2;
+ this.description = builder.description;
+ this.needReceipt = builder.needReceipt;
+ this.servicePhone = builder.servicePhone;
+ this.supportBuyForSelf = builder.supportBuyForSelf;
+ this.supportMulti = builder.supportMulti;
+ this.themeList = builder.themeList;
+ this.title = builder.title;
+ }
+
+ public static class Builder{
+ /**
+ * 货架ID,只在货架更新时用到
+ */
+ private String pageId;
+ /**
+ * 礼品卡货架名称
+ */
+ private String title;
+ /**
+ * 是否支持一次购买多张及发送至群,填true或者false,若填true则支持,默认为false
+ */
+ private Boolean supportMulti;
+ /**
+ * 礼品卡货架是否支持买给自己,填true或者false,若填true则支持,默认为false
+ */
+ private Boolean supportBuyForSelf;
+ /**
+ * 礼品卡货架主题页顶部banner图片,须先将图片上传至CDN,建议尺寸为750px*630px
+ */
+ private String bannerPicUrl;
+ /**
+ * 主题结构体
+ */
+ private JSONArray themeList;
+ /**
+ * 主题分类列表
+ */
+ private JSONArray categoryList;
+ /**
+ * 商家地址
+ */
+ private String address;
+ /**
+ * 商家服务电话
+ */
+ private String servicePhone;
+ /**
+ * 商家使用说明,用于描述退款、发票等流程
+ */
+ private String description;
+ /**
+ * 该货架的订单是否支持开发票,填true或者false,若填true则需要使用API设置支付后开票功能,默认为false
+ */
+ private Boolean needReceipt;
+ /**
+ * 商家自定义链接,用于承载退款、发票等流程
+ */
+ private JSONObject cell1;
+ /**
+ * 商家自定义链接,用于承载退款、发票等流程
+ */
+ private JSONObject cell2;
+
+ public Builder(){
+ this.themeList = new JSONArray();
+ this.categoryList = null;
+ }
+
+ public Builder pageId(String pageId){
+ this.pageId = pageId;
+ return this;
+ }
+
+ /**
+ * 设置礼品卡货架名称
+ *
+ * @param title
+ * @return
+ */
+ public Builder title(String title){
+ this.title = title;
+ return this;
+ }
+
+ /**
+ * 设置是否支持一次购买多张及发送至群
+ *
+ * @param supportMulti
+ * @return
+ */
+ public Builder supportMulti(boolean supportMulti){
+ this.supportMulti = Boolean.valueOf(supportMulti);
+ return this;
+ }
+
+ /**
+ * 设置礼品卡货架是否支持买给自己
+ *
+ * @param supportBuyForSelf
+ * @return
+ */
+ public Builder supportBuyForSelf(boolean supportBuyForSelf){
+ this.supportBuyForSelf = Boolean.valueOf(supportBuyForSelf);
+ return this;
+ }
+
+ /**
+ * 设置礼品卡货架主题页顶部banner图片
+ *
+ * @param url
+ * @return
+ */
+ public Builder bannerPicUrl(String url){
+ this.bannerPicUrl = url;
+ return this;
+ }
+
+ /**
+ * 添加一个主题
+ *
+ * @param theme
+ * @return
+ */
+ public Builder themeList(PageTheme theme){
+ this.themeList.add(theme);
+ return this;
+ }
+
+ /**
+ * 添加一个主题分类
+ *
+ * @param title
+ * @return
+ */
+ public Builder categoryList(String title){
+ if(this.categoryList==null){
+ this.categoryList = new JSONArray();
+ }
+ JSONObject category = new JSONObject();
+ category.put("title", title);
+ this.categoryList.add(category);
+ return this;
+ }
+
+ /**
+ * 设置商家地址
+ *
+ * @param address
+ * @return
+ */
+ public Builder address(String address){
+ this.address = address;
+ return this;
+ }
+
+ /**
+ * 设置商家服务电话
+ *
+ * @param phoneNo
+ * @return
+ */
+ public Builder servicePhone(String phoneNo){
+ this.servicePhone = phoneNo;
+ return this;
+ }
+
+ /**
+ * 设置商家使用说明
+ *
+ * @param description
+ * @return
+ */
+ public Builder description(String description){
+ this.description = description;
+ return this;
+ }
+
+ /**
+ * 设置该货架的订单是否支持开发票
+ *
+ * @param needReceipt
+ * @return
+ */
+ public Builder needReceipt(boolean needReceipt){
+ this.needReceipt = Boolean.valueOf(needReceipt);
+ return this;
+ }
+
+ /**
+ * 设置商家自定义链接
+ *
+ * @param title
+ * @param url
+ * @return
+ */
+ public Builder cell1(String title, String url){
+ JSONObject cell = new JSONObject();
+ cell.put("title", title);
+ cell.put("url", url);
+ this.cell1 = cell;
+ return this;
+ }
+
+ /**
+ * 设置商家自定义链接
+ *
+ * @param title
+ * @param url
+ * @return
+ */
+ public Builder cell2(String title, String url){
+ JSONObject cell = new JSONObject();
+ cell.put("title", title);
+ cell.put("url", url);
+ this.cell2 = cell;
+ return this;
+ }
+ }
+}
diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/model/card/PageTheme.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/model/card/PageTheme.java
new file mode 100644
index 00000000..94e6d847
--- /dev/null
+++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/model/card/PageTheme.java
@@ -0,0 +1,249 @@
+package com.foxinmy.weixin4j.model.card;
+
+import com.alibaba.fastjson.annotation.JSONField;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * 礼品卡货架主题
+ *
+ * @author kit(kit.li@qq.com)
+ * @date 2018年10月30日
+ */
+public class PageTheme {
+ /**
+ * 主题的封面图片,须先将图片上传至CDN 大小控制在1000px*600px
+ */
+ @JSONField(name = "theme_pic_url")
+ private String cover;
+ /**
+ * 主题名称,如“圣诞”“感恩家人”
+ */
+ private String title;
+ /**
+ * 主题title的颜色,直接传入色值
+ */
+ @JSONField(name = "title_color")
+ private String titleColor;
+ /**
+ * 礼品卡列表,表示主题可选择的礼品卡,由cardid及标题文字组成
+ */
+ @JSONField(name = "item_list")
+ private List itemList;
+ /**
+ * 礼品卡可选择的封面图
+ */
+ @JSONField(name = "pic_item_list")
+ private List picItemList;
+ /**
+ * 当前主题所属主题分类的索引,对应主题分类列表category_list内的title字段, 若填写了category_list则每个主题必填该序号
+ */
+ @JSONField(name = "category_index")
+ private Integer categoryIndex;
+ /**
+ * 该主题购买页是否突出商品名显示
+ */
+ @JSONField(name = "show_sku_title_first")
+ private Boolean showSkuTitleFirst;
+ /**
+ * 是否将当前主题设置为banner主题(主推荐)
+ */
+ @JSONField(name = "is_banner")
+ private Boolean bannerTheme;
+
+ public PageTheme(Builder builder){
+ this.cover = builder.cover;
+ this.title = builder.title;
+ this.titleColor = builder.titleColor;
+ this.itemList = builder.itemList;
+ this.picItemList = builder.picItemList;
+ this.categoryIndex = builder.categoryIndex;
+ this.showSkuTitleFirst = builder.showSkuTitleFirst;
+ this.bannerTheme = builder.bannerTheme;
+ }
+
+ public String getCover() {
+ return cover;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public String getTitleColor() {
+ return titleColor;
+ }
+
+ public List getItemList() {
+ return itemList;
+ }
+
+ public List getPicItemList() {
+ return picItemList;
+ }
+
+ public Integer getCategoryIndex() {
+ return categoryIndex;
+ }
+
+ public Boolean getShowSkuTitleFirst() {
+ return showSkuTitleFirst;
+ }
+
+ public Boolean getBannerTheme() {
+ return bannerTheme;
+ }
+
+ public static class Builder{
+ /**
+ * 主题的封面图片,须先将图片上传至CDN 大小控制在1000px*600px
+ */
+ private String cover;
+ /**
+ * 主题名称,如“圣诞”“感恩家人”
+ */
+ private String title;
+ /**
+ * 主题title的颜色,直接传入色值
+ */
+ private String titleColor;
+ /**
+ * 礼品卡列表,表示主题可选择的礼品卡,由cardid及标题文字组成
+ */
+ private List itemList;
+ /**
+ * 礼品卡可选择的封面图
+ */
+ private List picItemList;
+ /**
+ * 主题标号,对应category_list内的title字段, 若填写了category_list则每个主题必填该序号
+ */
+ private Integer categoryIndex;
+ /**
+ * 该主题购买页是否突出商品名显示
+ */
+ private Boolean showSkuTitleFirst;
+ /**
+ * 是否将当前主题设置为banner主题(主推荐)
+ */
+ private Boolean bannerTheme;
+
+ public Builder(){
+ this.itemList = new ArrayList();
+ this.picItemList = new ArrayList();
+ }
+
+ /**
+ * 设置主题的封面图片
+ *
+ * @param cover
+ * @return
+ */
+ public Builder cover(String cover){
+ this.cover = cover;
+ return this;
+ }
+
+ /**
+ * 设置主题名称
+ *
+ * @param title
+ * @return
+ */
+ public Builder title(String title){
+ this.title = title;
+ return this;
+ }
+
+ /**
+ * 设置主题title的颜色
+ *
+ * @param titleColor
+ * 直接设置色值,如:#FB966E
+ * @return
+ */
+ public Builder titleColor(String titleColor){
+ this.titleColor = titleColor;
+ return this;
+ }
+
+ /**
+ * 添加一个或多个礼品卡内容
+ *
+ * @param items
+ * @return
+ */
+ public Builder cardItems(CardItem... items){
+ this.itemList = Arrays.asList(items);
+ return this;
+ }
+
+ /**
+ * 添加一个礼品卡内容
+ *
+ * @param item
+ * @return
+ */
+ public Builder addCardItem(CardItem item){
+ this.itemList.add(item);
+ return this;
+ }
+
+ /**
+ * 添加一个或多个礼品卡封面图
+ *
+ * @param items
+ * @return
+ */
+ public Builder picItems(PicItem... items){
+ this.picItemList = Arrays.asList(items);
+ return this;
+ }
+
+ /**
+ * 添加一个礼品卡封面图
+ *
+ * @param item
+ * @return
+ */
+ public Builder addPicItem(PicItem item){
+ this.picItemList.add(item);
+ return this;
+ }
+
+ /**
+ * 设置所属主题分类的索引号
+ *
+ * @param index
+ * @return
+ */
+ public Builder categoryIndex(Integer index){
+ this.categoryIndex = index;
+ return this;
+ }
+
+ /**
+ * 设置该主题购买页是否突出商品名显示
+ *
+ * @param isShow
+ * @return
+ */
+ public Builder showSkuTitleFirst(Boolean isShow){
+ this.showSkuTitleFirst = isShow;
+ return this;
+ }
+
+ /**
+ * 设置是否将当前主题设置为banner主题(主推荐)
+ *
+ * @param isBanner
+ * @return
+ */
+ public Builder bannerTheme(Boolean isBanner){
+ this.bannerTheme = isBanner;
+ return this;
+ }
+ }
+}
diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/model/card/PicItem.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/model/card/PicItem.java
new file mode 100644
index 00000000..7b197057
--- /dev/null
+++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/model/card/PicItem.java
@@ -0,0 +1,66 @@
+package com.foxinmy.weixin4j.model.card;
+
+import com.alibaba.fastjson.annotation.JSONField;
+
+/**
+ * 礼品卡货架主题中的卡面结构体
+ *
+ * @author kit(kit.li@qq.com)
+ * @date 2018年10月30日
+ */
+public class PicItem {
+ /**
+ * 卡面图片,须先将图片上传至CDN,大小控制在1000 x 600像素以下
+ */
+ @JSONField(name = "background_pic_url")
+ private String backgroundPicUrl;
+ /**
+ * 自定义的卡面的标识, 非必填
+ */
+ @JSONField(name = "outer_img_id")
+ private String outerImgId;
+ /**
+ * 该卡面对应的默认祝福语,当用户没有编辑内容时会随卡默认填写为用户祝福内容
+ */
+ @JSONField(name = "default_gifting_msg")
+ private String defaultGiftingMsg;
+
+ public PicItem(){
+
+ }
+
+ public PicItem(String backgroundPicUrl, String defaultGiftingMsg) {
+ this.backgroundPicUrl = backgroundPicUrl;
+ this.defaultGiftingMsg = defaultGiftingMsg;
+ }
+
+ public PicItem(String backgroundPicUrl, String outerImgId, String defaultGiftingMsg) {
+ this.backgroundPicUrl = backgroundPicUrl;
+ this.outerImgId = outerImgId;
+ this.defaultGiftingMsg = defaultGiftingMsg;
+ }
+
+ public String getBackgroundPicUrl() {
+ return backgroundPicUrl;
+ }
+
+ public String getOuterImgId() {
+ return outerImgId;
+ }
+
+ public String getDefaultGiftingMsg() {
+ return defaultGiftingMsg;
+ }
+
+ public void setBackgroundPicUrl(String backgroundPicUrl) {
+ this.backgroundPicUrl = backgroundPicUrl;
+ }
+
+ public void setOuterImgId(String outerImgId) {
+ this.outerImgId = outerImgId;
+ }
+
+ public void setDefaultGiftingMsg(String defaultGiftingMsg) {
+ this.defaultGiftingMsg = defaultGiftingMsg;
+ }
+}
diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/model/card/VoucherCard.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/model/card/VoucherCard.java
new file mode 100644
index 00000000..bb549c39
--- /dev/null
+++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/model/card/VoucherCard.java
@@ -0,0 +1,122 @@
+package com.foxinmy.weixin4j.model.card;
+
+import com.alibaba.fastjson.annotation.JSONField;
+import com.foxinmy.weixin4j.type.card.CardType;
+import com.foxinmy.weixin4j.type.card.SubCardType;
+
+/**
+ * 单品类型礼品卡
+ * 指该礼品卡用于兑换指定单品,如汉堡礼品卡
+ *
+ * @className VoucherCardCoupon
+ * @author kit(kit.li@qq.com)
+ * @date 2018年10月23日
+ */
+public class VoucherCard extends CardCoupon {
+
+ /**
+ * 子卡券类型
+ */
+ @JSONField(name = "sub_card_type")
+ private final String subCardType;
+ /**
+ * 礼品卡背景图片,非必需
+ */
+ @JSONField(name = "background_pic_url")
+ private String backgroundPicUrl;
+ /**
+ * 是否支持积分,填写true或者false。默认为false
+ */
+ @JSONField(name = "supply_bonus")
+ private boolean supplyBonus;
+ /**
+ * 是否支持余额,填写true或者false。默认为false
+ */
+ @JSONField(name = "supply_balance")
+ private boolean supplyBalance;
+ /**
+ * 自定义会员信息类目,会员卡激活后显示,包含name和url字段
+ */
+ @JSONField(name = "custom_field1")
+ private MemCardCustomField customField1;
+ /**
+ * 自定义会员信息类目,会员卡激活后显示,包含name和url字段
+ */
+ @JSONField(name = "custom_field2")
+ private MemCardCustomField customField2;
+ /**
+ * 自定义会员信息类目,会员卡激活后显示,包含name和url字段
+ */
+ @JSONField(name = "custom_field3")
+ private MemCardCustomField customField3;
+ /**
+ * 是否自动激活,若开发者不需要额外激活流程则填写true。
+ */
+ @JSONField(name = "auto_activate")
+ private boolean autoActivate;
+
+ public String getSubCardType() {
+ return subCardType;
+ }
+
+ public String getBackgroundPicUrl() {
+ return backgroundPicUrl;
+ }
+
+ public boolean isSupplyBonus() {
+ return supplyBonus;
+ }
+
+ public boolean isSupplyBalance() {
+ return supplyBalance;
+ }
+
+ public MemCardCustomField getCustomField1() {
+ return customField1;
+ }
+
+ public MemCardCustomField getCustomField2() {
+ return customField2;
+ }
+
+ public MemCardCustomField getCustomField3() {
+ return customField3;
+ }
+
+ public boolean isAutoActivate() {
+ return autoActivate;
+ }
+
+ public VoucherCard(CouponBaseInfo baseInfo, GiftCard.Builder builder){
+ super(baseInfo);
+ this.subCardType = SubCardType.VOUCHER.name();
+ this.autoActivate = builder.isAutoActivate();
+ this.backgroundPicUrl = builder.getBackgroundPicUrl();
+ this.customField1 = builder.getCustomField1();
+ this.customField2 = builder.getCustomField2();
+ this.customField3 = builder.getCustomField3();
+ this.supplyBalance = false;
+ this.supplyBonus = builder.isSupplyBonus();;
+ }
+
+ @JSONField(serialize = false)
+ @Override
+ public CardType getCardType() {
+ return CardType.GENERAL_CARD;
+ }
+
+ @Override
+ public String toString() {
+ return "VoucherCardCoupon [" +
+ "subCardType='" + subCardType + '\'' +
+ ", backgroundPicUrl='" + backgroundPicUrl + '\'' +
+ ", supplyBonus=" + supplyBonus +
+ ", supplyBalance=" + supplyBalance +
+ ", customField1=" + customField1 +
+ ", customField2=" + customField2 +
+ ", customField3=" + customField3 +
+ ", autoActivate=" + autoActivate +
+ ", " + super.toString() +
+ ']';
+ }
+}
diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/type/card/CardType.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/type/card/CardType.java
index f9354e7f..9ae48928 100644
--- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/type/card/CardType.java
+++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/type/card/CardType.java
@@ -34,5 +34,10 @@ public enum CardType {
/**
* 会员卡
*/
- MEMBER_CARD;
+ MEMBER_CARD,
+
+ /**
+ * 通用(礼品)卡
+ */
+ GENERAL_CARD
}
diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/type/card/SubCardType.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/type/card/SubCardType.java
new file mode 100644
index 00000000..00d32183
--- /dev/null
+++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/type/card/SubCardType.java
@@ -0,0 +1,19 @@
+package com.foxinmy.weixin4j.type.card;
+
+/**
+ * 礼品卡类型
+ *
+ * @className SubCardType
+ * @author kit(kit.li@qq.com)
+ * @date 2018年10月23日
+ */
+public enum SubCardType {
+ /**
+ * 礼品卡
+ */
+ GIFT_CARD,
+ /**
+ * 兑换卡
+ */
+ VOUCHER
+}
diff --git a/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/WeixinProxy.java b/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/WeixinProxy.java
index d527d39d..4b38057f 100644
--- a/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/WeixinProxy.java
+++ b/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/WeixinProxy.java
@@ -4,6 +4,7 @@ import java.io.InputStream;
import java.util.Date;
import java.util.List;
+import com.alibaba.fastjson.JSONObject;
import com.foxinmy.weixin4j.cache.CacheStorager;
import com.foxinmy.weixin4j.cache.FileCacheStorager;
import com.foxinmy.weixin4j.exception.WeixinException;
@@ -14,6 +15,7 @@ import com.foxinmy.weixin4j.model.WeixinAccount;
import com.foxinmy.weixin4j.model.card.CardCoupon;
import com.foxinmy.weixin4j.model.card.CardCoupons;
import com.foxinmy.weixin4j.model.card.CardQR;
+import com.foxinmy.weixin4j.model.card.GiftCardPage;
import com.foxinmy.weixin4j.model.media.MediaCounter;
import com.foxinmy.weixin4j.model.media.MediaDownloadResult;
import com.foxinmy.weixin4j.model.media.MediaItem;
@@ -2010,6 +2012,17 @@ public class WeixinProxy {
return cardApi.createCardCoupon(cardCoupon);
}
+ /**
+ * 查询某个card_id的创建信息、审核状态以及库存数量。
+ *
+ * @param cardId
+ * @return
+ * @throws WeixinException
+ */
+ public JSONObject getCardInfo(String cardId) throws WeixinException {
+ return cardApi.getCardInfo(cardId);
+ }
+
/**
* 设置卡券买单:创建卡券之后,开发者可以通过设置微信买单接口设置该card_id支持微信买单功能。值得开发者注意的是,
* 设置买单的card_id必须已经配置了门店,否则会报错。
@@ -2064,6 +2077,200 @@ public class WeixinProxy {
return cardApi.createCardQR(expireSeconds, cardQRs);
}
+ /**
+ * 微信礼品卡货架创建接口,开发者可以通过该接口创建一个礼品卡货架并且用于公众号、门店的礼品卡售卖。
+ *
+ * @param page
+ * 货架对象
+ * @return 货架ID
+ * @throws WeixinException
+ * @see 微信礼品卡
+ */
+ public String addGiftCardPage(GiftCardPage page) throws WeixinException {
+ return cardApi.addGiftCardPage(page);
+ }
+
+ /**
+ * 查询礼品卡货架信息接口
+ *
+ * @param pageId
+ * 货架ID
+ * @return
+ * @throws WeixinException
+ */
+ public JSONObject getGiftCardPage(String pageId) throws WeixinException {
+ return cardApi.getGiftCardPage(pageId);
+ }
+
+ /**
+ * 下架一个礼品卡货架
+ *
+ * @param pageId
+ * @return
+ * @throws WeixinException
+ */
+ public ApiResult maintainGiftCardPage(String pageId) throws WeixinException {
+ return cardApi.maintainGiftCardPage(pageId);
+ }
+
+ /**
+ * 下架所有礼品卡货架
+ *
+ * @return
+ * @throws WeixinException
+ */
+ public ApiResult maintainAllGiftCardPage() throws WeixinException {
+ return cardApi.maintainAllGiftCardPage();
+ }
+
+ /**
+ * 查询当前商户下所有的礼品卡货架id
+ *
+ * @return
+ * @throws WeixinException
+ */
+ public String[] getGiftCardPageIdList() throws WeixinException {
+ return cardApi.getGiftCardPageIdList();
+ }
+
+ /**
+ * 修改礼品卡货架信息接口
+ *
+ * @param page
+ * 货架对象
+ * @return
+ * @throws WeixinException
+ */
+ public ApiResult updateGiftCardPage(GiftCardPage page) throws WeixinException {
+ return cardApi.updateGiftCardPage(page);
+ }
+
+ /**
+ * 申请礼品卡的微信支付权限
+ * @param subMchId
+ * 子商户号
+ * @return 微信支付商户平台确认地址
+ * @throws WeixinException
+ */
+ public String addGiftCardPayWhitelist(String subMchId) throws WeixinException {
+ return cardApi.addGiftCardPayWhitelist(subMchId);
+ }
+
+ /**
+ * 绑定商户号到礼品卡小程序
+ *
+ * @param wxaAppid
+ * 微信小程序ID
+ * @param subMchId
+ * 微信支付商户号
+ * @return
+ * @throws WeixinException
+ */
+ public ApiResult bindGiftCardPaySubMch(String wxaAppid, String subMchId) throws WeixinException {
+ return cardApi.bindGiftCardPaySubMch(wxaAppid, subMchId);
+ }
+
+ /**
+ * 上传礼品卡小程序代码
+ *(提供小程序APPID及货架ID,由微信平台为你小程序帐号上传一套现成的礼品卡小程序,直接用于礼品卡售卖)
+ *
+ * @param wxaAppid
+ * 微信小程序APPID
+ * @param pageId
+ * 礼品卡货架ID
+ * @return
+ * @throws WeixinException
+ */
+ public ApiResult setGiftCardWxaCode(String wxaAppid, String pageId) throws WeixinException {
+ return cardApi.setGiftCardWxaCode(wxaAppid, pageId);
+ }
+
+ /**
+ * 当礼品卡被使用完毕或者发生转存、绑定等操作后,开发者可以通过该接口核销用户的礼品卡,使礼品卡在列表中沉底并不再被使用。
+ * 注意:需在礼品卡核销前调用,否则会报40099 已核销的错误
+ *
+ * @param code
+ * 卡券Code码。
+ * @param cardId
+ * 卡券ID,自定义code卡券必填,否则非必填。
+ * @return
+ * @throws WeixinException
+ */
+ public ApiResult consumeGiftCard(String code, String cardId) throws WeixinException {
+ return cardApi.consumeGiftCard(code, cardId);
+ }
+
+ /**
+ * 开发者可以通过该接口查询到code对应的信息,如余额、有效期、订单号等,主要用于防止在交易完成后丢单的情况下,用于核销/余额变动时兜底处理。
+ *
+ * @param code
+ * 卡券Code码
+ * @param cardId
+ * 卡券ID,自定义code卡券必填,否则非必填。
+ * @return
+ * @throws WeixinException
+ */
+ public JSONObject getGiftCardInfo(String code, String cardId) throws WeixinException {
+ return cardApi.getGiftCardInfo(code, cardId);
+ }
+
+ /**
+ * 查询某个订单号对应的订单详情
+ *
+ * @param orderId
+ * 礼品卡订单号,商户可以通过购买成功的事件推送或者批量查询订单接口获得
+ * @return
+ * @throws WeixinException
+ */
+ public JSONObject getGiftCardOrderInfo(String orderId) throws WeixinException {
+ return cardApi.getOrderInfo(orderId);
+ }
+
+ /**
+ * 批量查询礼品卡订单信息接口
+ *
+ * @param beginTime
+ * 查询的时间起点,十位时间戳(utc+8)
+ * @param endTime
+ * 查询的时间终点,十位时间戳(utc+8)
+ * @param sortType
+ * 填"ASC" / "DESC",表示对订单创建时间进行“升 / 降”排序
+ * @param offset
+ * 查询的订单偏移量,如填写100则表示从第100个订单开始拉取
+ * @param limit
+ * 查询订单的数量,如offset填写100,count填写10,则表示查询第100个到第110个订单
+ * @return
+ * @throws WeixinException
+ */
+ public JSONObject getGiftCardOrders(long beginTime, long endTime, String sortType, int offset, int limit) throws WeixinException
+ {
+ return cardApi.getOrders(beginTime, endTime, sortType, offset, limit);
+ }
+
+ /**
+ * 更新用户礼品卡信息
+ * 当礼品卡被使用后,可以通过该接口变更某个礼品卡的余额信息。
+ *
+ * @param cardInfo
+ * @return
+ * @throws WeixinException
+ */
+ public JSONObject updateGiftCardUserBalance(CardInfo cardInfo) throws WeixinException {
+ return cardApi.updateGiftCardUserBalance(cardInfo);
+ }
+
+ /**
+ * 对一笔礼品卡订单操作退款
+ *
+ * @param orderId
+ * 订单ID
+ * @return
+ * @throws WeixinException
+ */
+ public ApiResult giftCardOrderRefund(String orderId) throws WeixinException {
+ return cardApi.orderRefund(orderId);
+ }
+
/**
* 打开/关闭已群发文章评论
*
diff --git a/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/CardApi.java b/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/CardApi.java
index 79af30b5..3b84c8c1 100644
--- a/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/CardApi.java
+++ b/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/CardApi.java
@@ -4,19 +4,14 @@ import java.io.IOException;
import java.util.List;
import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.http.weixin.ApiResult;
import com.foxinmy.weixin4j.http.weixin.WeixinResponse;
import com.foxinmy.weixin4j.model.Token;
-import com.foxinmy.weixin4j.model.card.CardCoupon;
-import com.foxinmy.weixin4j.model.card.CardCoupons;
-import com.foxinmy.weixin4j.model.card.CardQR;
-import com.foxinmy.weixin4j.model.card.MemberInitInfo;
-import com.foxinmy.weixin4j.model.card.MemberUpdateInfo;
-import com.foxinmy.weixin4j.model.card.MemberUserForm;
-import com.foxinmy.weixin4j.model.card.MemberUserInfo;
+import com.foxinmy.weixin4j.model.card.*;
import com.foxinmy.weixin4j.model.qr.QRParameter;
import com.foxinmy.weixin4j.model.qr.QRResult;
import com.foxinmy.weixin4j.token.TokenManager;
@@ -223,6 +218,25 @@ public class CardApi extends MpApi {
return CardStatus.valueOf(status);
}
+ /**
+ * 查询某个card_id的创建信息、审核状态以及库存数量。
+ *
+ * @param cardId
+ * @return
+ * @throws WeixinException
+ */
+ public JSONObject getCardInfo(String cardId) throws WeixinException {
+ JSONObject requestObj = new JSONObject();
+ requestObj.put("card_id", cardId);
+ String card_get_uri = getRequestUri("card_get_uri");
+ Token token = tokenManager.getCache();
+ WeixinResponse response = weixinExecutor.post(
+ String.format(card_get_uri, token.getAccessToken()),
+ requestObj.toJSONString());
+ JSONObject responseJson = response.getAsJson();
+ return responseJson.getJSONObject("card");
+ }
+
/**
* 支持更新所有卡券类型的部分通用字段及特殊卡券(会员卡、飞机票、电影票、会议门票)中特定字段的信息。
*
@@ -330,4 +344,336 @@ public class CardApi extends MpApi {
token.getAccessToken()), JSON.toJSONString(updateInfo));
return response.getAsJson();
}
+
+ /**
+ * 创建一个礼品卡货架
+ *
+ * @param page
+ * @return 货架ID
+ * @throws WeixinException
+ */
+ public String addGiftCardPage(GiftCardPage page) throws WeixinException {
+ String card_gift_card_page_add = getRequestUri("card_gift_card_page_add_uri");
+ JSONObject pageJson = new JSONObject();
+ pageJson.put("page", page);
+ Token token = tokenManager.getCache();
+ WeixinResponse response = weixinExecutor.post(
+ String.format(card_gift_card_page_add,
+ token.getAccessToken()), JSON.toJSONString(pageJson));
+ JSONObject jsonObject = response.getAsJson();
+ return jsonObject.getString("page_id");
+ }
+
+ /**
+ * 查询礼品卡货架信息
+ *
+ * @param pageId
+ * 货架ID
+ * @return
+ * @throws WeixinException
+ */
+ public JSONObject getGiftCardPage(String pageId) throws WeixinException {
+ String card_gift_card_page_get = getRequestUri("card_gift_card_page_get_uri");
+ JSONObject param = new JSONObject();
+ param.put("page_id", pageId);
+ Token token = tokenManager.getCache();
+ WeixinResponse response = weixinExecutor.post(
+ String.format(card_gift_card_page_get,
+ token.getAccessToken()), JSON.toJSONString(param));
+ JSONObject jsonObject = response.getAsJson();
+
+ return jsonObject.getJSONObject("page");
+ }
+
+ /**
+ * 查询当前商户下所有的礼品卡货架id
+ *
+ * @return
+ * @throws WeixinException
+ */
+ public String[] getGiftCardPageIdList() throws WeixinException {
+ String card_gift_card_page_batchget = getRequestUri("card_gift_card_page_batchget_uri");
+ JSONObject param = new JSONObject();
+ Token token = tokenManager.getCache();
+ WeixinResponse response = weixinExecutor.post(
+ String.format(card_gift_card_page_batchget,
+ token.getAccessToken()), JSON.toJSONString(param));
+ JSONObject jsonObject = response.getAsJson();
+ JSONArray idList = jsonObject.getJSONArray("page_id_list");
+ if(idList==null || idList.size()==0){
+ return new String[0];
+ }
+
+ return idList.toArray(new String[idList.size()]);
+ }
+
+ /**
+ * 下架礼品卡货架
+ *
+ * @param pageId
+ * 礼品卡货架ID
+ * @return
+ * @throws WeixinException
+ */
+ public ApiResult maintainGiftCardPage(String pageId) throws WeixinException {
+ String card_gift_card_maintain_set = getRequestUri("card_gift_card_maintain_set_uri");
+ JSONObject param = new JSONObject();
+ param.put("page_id", pageId);
+ param.put("maintain", true);
+ Token token = tokenManager.getCache();
+ WeixinResponse response = weixinExecutor.post(
+ String.format(card_gift_card_maintain_set,
+ token.getAccessToken()), JSON.toJSONString(param));
+ return response.getAsResult();
+ }
+
+ /**
+ * 下架所有礼品卡货架
+ *
+ * @return
+ * @throws WeixinException
+ */
+ public ApiResult maintainAllGiftCardPage() throws WeixinException {
+ String card_gift_card_maintain_set = getRequestUri("card_gift_card_maintain_set_uri");
+ JSONObject param = new JSONObject();
+ param.put("all", true);
+ param.put("maintain", true);
+ Token token = tokenManager.getCache();
+ WeixinResponse response = weixinExecutor.post(
+ String.format(card_gift_card_maintain_set,
+ token.getAccessToken()), JSON.toJSONString(param));
+ return response.getAsResult();
+ }
+
+ /**
+ * 查询某个订单号对应的订单详情
+ *
+ * @param orderId
+ * 礼品卡订单号,商户可以通过购买成功的事件推送或者批量查询订单接口获得
+ * @return
+ * @throws WeixinException
+ */
+ public JSONObject getOrderInfo(String orderId) throws WeixinException {
+ String card_gift_card_order_get = getRequestUri("card_gift_card_order_get_uri");
+ JSONObject param = new JSONObject();
+ param.put("order_id", orderId);
+ Token token = tokenManager.getCache();
+ WeixinResponse response = weixinExecutor.post(
+ String.format(card_gift_card_order_get,
+ token.getAccessToken()), JSON.toJSONString(param));
+
+ return response.getAsJson();
+ }
+
+ /**
+ * 批量查询礼品卡订单信息接口
+ *
+ * @param beginTime
+ * 查询的时间起点,十位时间戳(utc+8)
+ * @param endTime
+ * 查询的时间终点,十位时间戳(utc+8)
+ * @param sortType
+ * 填"ASC" / "DESC",表示对订单创建时间进行“升 / 降”排序
+ * @param offset
+ * 查询的订单偏移量,如填写100则表示从第100个订单开始拉取
+ * @param limit
+ * 查询订单的数量,如offset填写100,count填写10,则表示查询第100个到第110个订单
+ * @return
+ * @throws WeixinException
+ */
+ public JSONObject getOrders(long beginTime, long endTime, String sortType, int offset, int limit) throws WeixinException {
+ String card_gift_card_order_batchget_uri = getRequestUri("card_gift_card_order_batchget_uri");
+ JSONObject param = new JSONObject();
+ param.put("begin_time", beginTime);
+ param.put("end_time", endTime);
+ param.put("sort_type", sortType);
+ param.put("offset", offset);
+ param.put("count", limit);
+ Token token = tokenManager.getCache();
+ WeixinResponse response = weixinExecutor.post(
+ String.format(card_gift_card_order_batchget_uri,
+ token.getAccessToken()), JSON.toJSONString(param));
+
+ return response.getAsJson();
+ }
+
+ /**
+ * 更新礼品卡货架
+ *
+ * @param page
+ * @return
+ * @throws WeixinException
+ */
+ public ApiResult updateGiftCardPage(GiftCardPage page) throws WeixinException {
+ String card_gift_card_page_update_uri = getRequestUri("card_gift_card_page_update_uri");
+ JSONObject pageJson = new JSONObject();
+ pageJson.put("page", page);
+ Token token = tokenManager.getCache();
+ WeixinResponse response = weixinExecutor.post(
+ String.format(card_gift_card_page_update_uri,
+ token.getAccessToken()), JSON.toJSONString(pageJson));
+ return response.getAsResult();
+ }
+
+ /**
+ * 申请礼品卡的微信支付权限
+ *
+ * @param subMchId
+ * 微信支付子商户号,须为普通服务商模式或者直连商户号,建议为礼品卡专用商户号;商户号必须为公众号申请的商户号
+ * 公众号须与商户号同主体,非同主体情况须和对接人联系申请
+ * @return 商户平台确认地址,请获得后点击打开登录商户平台后台并点击确认
+ * @throws WeixinException
+ */
+ public String addGiftCardPayWhitelist(String subMchId) throws WeixinException{
+ String card_gift_card_pay_whitelist_add = getRequestUri("card_gift_card_pay_whitelist_add_uri");
+ JSONObject param = new JSONObject();
+ param.put("sub_mch_id", subMchId);
+ Token token = tokenManager.getCache();
+ WeixinResponse response = weixinExecutor.post(
+ String.format(card_gift_card_pay_whitelist_add,
+ token.getAccessToken()), JSON.toJSONString(param));
+ JSONObject jsonObject = response.getAsJson();
+ return jsonObject.getString("url");
+ }
+
+ /**
+ * 绑定商户号到礼品卡小程序
+ *
+ * @param wxaAppid
+ * 礼品卡小程序APPID
+ * @param subMchId
+ * 微信支付商户号
+ * @return
+ * @throws WeixinException
+ */
+ public ApiResult bindGiftCardPaySubMch(String wxaAppid, String subMchId) throws WeixinException {
+ String card_gift_card_pay_submch_bind = getRequestUri("card_gift_card_pay_submch_bind_uri");
+ JSONObject param = new JSONObject();
+ param.put("sub_mch_id", subMchId);
+ param.put("wxa_appid", wxaAppid);
+
+ Token token = tokenManager.getCache();
+ WeixinResponse response = weixinExecutor.post(
+ String.format(card_gift_card_pay_submch_bind,
+ token.getAccessToken()), JSON.toJSONString(param));
+
+ return response.getAsResult();
+ }
+
+ /**
+ * 上传礼品卡小程序代码
+ * (提供小程序APPID及货架ID,由微信平台为你小程序帐号上传一套现成的礼品卡小程序,直接用于礼品卡售卖)
+ *
+ * @param wxaAppid
+ * 微信小程序APPID
+ * @param pageId
+ * 礼品卡货架ID
+ * @return
+ * @throws WeixinException
+ */
+ public ApiResult setGiftCardWxaCode(String wxaAppid, String pageId) throws WeixinException {
+ String card_gift_card_wxa_set = getRequestUri("card_gift_card_wxa_set_uri");
+ JSONObject param = new JSONObject();
+ param.put("wxa_appid", wxaAppid);
+ param.put("page_id", pageId);
+
+ Token token = tokenManager.getCache();
+ WeixinResponse response = weixinExecutor.post(
+ String.format(card_gift_card_wxa_set,
+ token.getAccessToken()), JSON.toJSONString(param));
+
+ return response.getAsResult();
+ }
+
+ /**
+ * 更新用户礼品卡信息
+ * 当礼品卡被使用后,可以通过该接口变更某个礼品卡的余额信息。
+ *
+ * @param cardInfo
+ * @return
+ * @throws WeixinException
+ */
+ public JSONObject updateGiftCardUserBalance(CardInfo cardInfo) throws WeixinException {
+ String card_gift_card_wxa_set = getRequestUri("card_general_card_update_user_uri");
+
+ Token token = tokenManager.getCache();
+ WeixinResponse response = weixinExecutor.post(
+ String.format(card_gift_card_wxa_set,
+ token.getAccessToken()), JSON.toJSONString(cardInfo));
+ return response.getAsJson();
+ }
+
+ /**
+ * 当礼品卡被使用完毕或者发生转存、绑定等操作后,开发者可以通过该接口核销用户的礼品卡,使礼品卡在列表中沉底并不再被使用。
+ *
+ * @param code
+ * 卡券Code码。
+ * @param cardId
+ * 卡券ID,自定义code卡券必填,否则非必填。
+ * @return
+ * @throws WeixinException
+ */
+ public ApiResult consumeGiftCard(String code, String cardId) throws WeixinException {
+ String card_code_consume = getRequestUri("card_code_consume_uri");
+ JSONObject param = new JSONObject();
+ param.put("code", code);
+ if(cardId!=null && cardId.length()>0){
+ param.put("card_id", cardId);
+ }
+
+ Token token = tokenManager.getCache();
+ WeixinResponse response = weixinExecutor.post(
+ String.format(card_code_consume,
+ token.getAccessToken()), JSON.toJSONString(param));
+
+ return response.getAsResult();
+ }
+
+ /**
+ * 开发者可以通过该接口查询到code对应的信息,如余额、有效期、订单号等,主要用于防止在交易完成后丢单的情况下,用于核销/余额变动时兜底处理。
+ * 注意:需在礼品卡核销前调用,否则会报40099 已核销的错误
+ *
+ * @param code
+ * 卡券Code码
+ * @param cardId
+ * 卡券ID,自定义code卡券必填,否则非必填。
+ * @return
+ * @throws WeixinException
+ */
+ public JSONObject getGiftCardInfo(String code, String cardId) throws WeixinException {
+ String card_code_get = getRequestUri("card_code_get_uri");
+ JSONObject param = new JSONObject();
+ param.put("code", code);
+ if(!StringUtils.isEmpty(cardId)){
+ param.put("card_id", cardId);
+ }
+
+ Token token = tokenManager.getCache();
+ WeixinResponse response = weixinExecutor.post(
+ String.format(card_code_get,
+ token.getAccessToken()), JSON.toJSONString(param));
+
+ return response.getAsJson();
+ }
+
+ /**
+ * 对一笔礼品卡订单操作退款
+ *
+ * @param orderId
+ * 订单ID
+ * @return
+ * @throws WeixinException
+ */
+ public ApiResult orderRefund(String orderId) throws WeixinException {
+ String card_gift_card_order_refund_uri = getRequestUri("card_gift_card_order_refund_uri");
+ JSONObject param = new JSONObject();
+ param.put("order_id", orderId);
+
+ Token token = tokenManager.getCache();
+ WeixinResponse response = weixinExecutor.post(
+ String.format(card_gift_card_order_refund_uri,
+ token.getAccessToken()), JSON.toJSONString(param));
+
+ return response.getAsResult();
+ }
}
diff --git a/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/weixin.properties b/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/weixin.properties
index 98e3c622..e3682654 100644
--- a/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/weixin.properties
+++ b/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/weixin.properties
@@ -219,6 +219,34 @@ card_member_card_activate_user_form_uri={api_base_url}/card/membercard/activateu
card_member_card_user_info_uri={api_base_url}/card/membercard/userinfo/get?access_token=%s
#\u66f4\u65b0\u4f1a\u5458\u4fe1\u606f
card_member_card_update_user_uri={api_base_url}/card/membercard/updateuser?access_token=%s
+# \u521B\u5EFA\u793C\u54C1\u5361\u8D27\u67B6
+card_gift_card_page_add_uri={api_base_url}/card/giftcard/page/add?access_token=%s
+# \u83B7\u53D6\u8D27\u67B6\u4FE1\u606F
+card_gift_card_page_get_uri={api_base_url}/card/giftcard/page/get?access_token=%s
+# \u67E5\u8BE2\u8D27\u67B6\u5217\u8868\u4FE1\u606F
+card_gift_card_page_batchget_uri={api_base_url}/card/giftcard/page/batchget?access_token=%s
+# \u4E0B\u67B6\u793C\u54C1\u5361\u8D27\u67B6
+card_gift_card_maintain_set_uri={api_base_url}/card/giftcard/maintain/set?access_token=%s
+# \u793C\u54C1\u5361\u8D27\u67B6\u66F4\u65B0
+card_gift_card_page_update_uri={api_base_url}/card/giftcard/page/update?access_token=%s
+# \u7533\u8BF7\u793C\u54C1\u5361\u5FAE\u4FE1\u652F\u4ED8\u6743\u9650
+card_gift_card_pay_whitelist_add_uri={api_base_url}/card/giftcard/pay/whitelist/add?access_token=%s
+# \u7ED1\u5B9A\u5546\u6237\u53F7\u5230\u793C\u54C1\u5361\u5C0F\u7A0B\u5E8F\u63A5\u53E3
+card_gift_card_pay_submch_bind_uri={api_base_url}/card/giftcard/pay/submch/bind?access_token=%s
+# \u4E0A\u4F20\u5C0F\u7A0B\u5E8F\u4EE3\u7801
+card_gift_card_wxa_set_uri={api_base_url}/card/giftcard/wxa/set?access_token=%s
+# \u6838\u9500\u793C\u54C1\u5361
+card_code_consume_uri={api_base_url}/card/code/consume?access_token=%s
+# \u67E5\u8BE2\u793C\u54C1\u5361
+card_code_get_uri={api_base_url}/card/code/get?access_token=%s
+# \u67E5\u8BE2\u5355\u4E2A\u8BA2\u5355
+card_gift_card_order_get_uri={api_base_url}/card/giftcard/order/get?access_token=%s
+# \u67E5\u8BE2\u6307\u5B9A\u5546\u6237\u67D0\u4E2A\u65F6\u95F4\u6BB5\u5185\u521B\u5EFA\u7684\u6240\u6709\u793C\u54C1\u5361\u8BA2\u5355\u8BE6\u60C5\u3002
+card_gift_card_order_batchget_uri={api_base_url}/card/giftcard/order/batchget?access_token=%s
+# \u66F4\u65B0\u7528\u6237\u793C\u54C1\u5361\u4FE1\u606F\u63A5\u53E3
+card_general_card_update_user_uri={api_base_url}/card/generalcard/updateuser?access_token=%s
+# \u793C\u54C1\u5361\u8BA2\u5355\u9000\u6B3E
+card_gift_card_order_refund_uri={api_base_url}/card/giftcard/order/refund?access_token=%s
# \u4f7f\u7528\u6388\u6743\u7801\u6362\u53d6\u516c\u4f17\u53f7\u7684\u63a5\u53e3\u8c03\u7528\u51ed\u636e\u548c\u6388\u6743\u4fe1\u606f
component_query_authorization_uri={api_cgi_url}/component/api_query_auth?component_access_token=%s