Skip to content

Commit

Permalink
Merge pull request foxinmy#174 from kit-lee/feature/giftcard
Browse files Browse the repository at this point in the history
微信卡券礼品卡相关API
  • Loading branch information
foxinmy authored Jun 13, 2019
2 parents 08ab666 + 130370f commit be13fb4
Show file tree
Hide file tree
Showing 17 changed files with 2,261 additions and 42 deletions.
37 changes: 23 additions & 14 deletions weixin4j-base/src/main/java/com/foxinmy/weixin4j/api/MchApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}
Expand Down Expand Up @@ -93,21 +95,28 @@ public WeixinSignature getWeixinSignature() {
*/
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 (is == null) {
throw new WeixinException("Invalid certificate file : " + certificate.toString());
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);
}
this.weixinSSLExecutor = weixinExecutor.createSSLRequestExecutor(weixinAccount.getCertificateKey(), is);
} catch (IOException e) {
throw new WeixinException("IO Error on createSSLRequestExecutor", e);
}
}
return this.weixinSSLExecutor;
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -29,6 +39,8 @@
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;

/**
* 负责微信请求的执行
Expand Down Expand Up @@ -56,6 +68,18 @@ public WeixinRequestExecutor(HttpParams params) {
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方法执行微信请求
*
Expand Down Expand Up @@ -250,4 +274,69 @@ public WeixinRequestExecutor createSSLRequestExecutor(SSLContext sslContext) {
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));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,30 @@ public static CouponAdvanceInfo.Builder customAdvance() {
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(); }

/**
* 创建代金券
*
Expand Down Expand Up @@ -122,4 +146,32 @@ public static MemberCard createMemberCard(CouponBaseInfo.Builder baseBuilder, Me
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;
}
}
Loading

0 comments on commit be13fb4

Please sign in to comment.