Comprehensive Java SDK for WeChat MiniApp development with complete platform integration
—
Comprehensive configuration management supporting memory-based and Redis-based storage with multi-app support, access token management, and production-ready deployment options for WeChat MiniApp applications.
Core configuration interface providing access to all WeChat MiniApp settings and credentials.
public interface WxMaConfig {
// Basic Configuration
String getAppid();
void setAppid(String appid);
String getSecret();
void setSecret(String secret);
String getToken();
void setToken(String token);
String getAesKey();
void setAesKey(String aesKey);
String getOriginalId();
void setOriginalId(String originalId);
String getCloudEnv();
void setCloudEnv(String cloudEnv);
// Access Token Management
String getAccessToken();
Lock getAccessTokenLock();
boolean isAccessTokenExpired();
void expireAccessToken();
void updateAccessToken(WxAccessToken accessToken);
void updateAccessToken(String accessToken, int expiresInSeconds);
// Stable Access Token Support
boolean isStableAccessToken();
void useStableAccessToken(boolean use);
// JSAPI Ticket Management
String getJsapiTicket();
Lock getJsapiTicketLock();
boolean isJsapiTicketExpired();
void expireJsapiTicket();
void updateJsapiTicket(String jsapiTicket, int expiresInSeconds);
// Card API Ticket Management
String getCardApiTicket();
Lock getCardApiTicketLock();
boolean isCardApiTicketExpired();
void expireCardApiTicket();
void updateCardApiTicket(String cardApiTicket, int expiresInSeconds);
// HTTP Configuration
String getHttpProxyHost();
void setHttpProxyHost(String httpProxyHost);
int getHttpProxyPort();
void setHttpProxyPort(int httpProxyPort);
String getHttpProxyUsername();
void setHttpProxyUsername(String httpProxyUsername);
String getHttpProxyPassword();
void setHttpProxyPassword(String httpProxyPassword);
// HTTP Client Configuration
int getRetrySleepMillis();
void setRetrySleepMillis(int retrySleepMillis);
int getMaxRetryTimes();
void setMaxRetryTimes(int maxRetryTimes);
ApacheHttpClientBuilder getApacheHttpClientBuilder();
void setApacheHttpClientBuilder(ApacheHttpClientBuilder apacheHttpClientBuilder);
// API Signature (for server-side API)
String getApiSignatureRsaPrivateKey();
void setApiSignatureRsaPrivateKey(String apiSignatureRsaPrivateKey);
String getApiSignatureAesKey();
void setApiSignatureAesKey(String apiSignatureAesKey);
String getApiSignatureRsaPrivateKeySn();
void setApiSignatureRsaPrivateKeySn(String apiSignatureRsaPrivateKeySn);
String getApiSignatureAesKeySn();
void setApiSignatureAesKeySn(String apiSignatureAesKeySn);
// Third-party Platform Support
String getWechatMpAppid();
void setWechatMpAppid(String wechatMpAppid);
}Memory-based configuration implementation suitable for single-instance deployments.
public class WxMaDefaultConfigImpl implements WxMaConfig, Serializable {
// Configuration Storage
private String appid; // Mini program App ID
private String secret; // Mini program App Secret
private String token; // Message encryption token
private String aesKey; // Message encryption AES key
private String originalId; // Original ID
private String cloudEnv; // Cloud environment ID
// Access Token Management
private volatile String accessToken; // Current access token
private volatile long accessTokenExpiresTime; // Expiration timestamp
private final Lock accessTokenLock = new ReentrantLock();
private boolean stableAccessToken = false; // Use stable access token interface
// Token Update Callbacks
private Consumer<WxAccessTokenEntity> updateAccessTokenBefore;
private boolean enableUpdateAccessTokenBefore = false;
// JSAPI Ticket Management
private volatile String jsapiTicket; // JSAPI ticket
private volatile long jsapiTicketExpiresTime; // JSAPI ticket expiration
private final Lock jsapiTicketLock = new ReentrantLock();
// Card API Ticket Management
private volatile String cardApiTicket; // Card API ticket
private volatile long cardApiTicketExpiresTime; // Card API ticket expiration
private final Lock cardApiTicketLock = new ReentrantLock();
// HTTP Configuration
private String httpProxyHost; // HTTP proxy host
private int httpProxyPort = 0; // HTTP proxy port
private String httpProxyUsername; // HTTP proxy username
private String httpProxyPassword; // HTTP proxy password
private int retrySleepMillis = 1000; // Retry delay in milliseconds
private int maxRetryTimes = 5; // Maximum retry attempts
// HTTP Client Builder
private ApacheHttpClientBuilder apacheHttpClientBuilder;
// API Signature Configuration
private String apiSignatureRsaPrivateKey; // RSA private key for API signature
private String apiSignatureAesKey; // AES key for API signature
private String apiSignatureRsaPrivateKeySn; // RSA key serial number
private String apiSignatureAesKeySn; // AES key serial number
// Third-party Platform
private String wechatMpAppid; // Component app ID for third-party platforms
// Constructors
public WxMaDefaultConfigImpl();
// Token Management Methods
@Override
public boolean isAccessTokenExpired();
@Override
public void updateAccessToken(String accessToken, int expiresInSeconds);
@Override
public void updateAccessToken(WxAccessToken accessToken);
// Callback Configuration
public void setUpdateAccessTokenBefore(Consumer<WxAccessTokenEntity> updateAccessTokenBefore);
public void enableUpdateAccessTokenBefore(boolean enable);
// All getters and setters...
}Production-ready Redis-based configuration implementations for distributed deployments.
// Basic Redis Configuration
public class WxMaRedisConfigImpl extends AbstractWxMaRedisConfig {
private final RedisTemplate<String, String> redisTemplate;
public WxMaRedisConfigImpl(RedisTemplate<String, String> redisTemplate);
public WxMaRedisConfigImpl(RedisTemplate<String, String> redisTemplate, String keyPrefix);
// Redis operations using RedisTemplate
@Override
public String getConfigValue(String key, String defaultValue);
@Override
public void setConfigValue(String key, String value);
@Override
public void expireConfigValue(String key, int seconds);
}
// Redisson Configuration
public class WxMaRedissonConfigImpl extends AbstractWxMaRedisConfig {
private final RedissonClient redissonClient;
public WxMaRedissonConfigImpl(RedissonClient redissonClient);
public WxMaRedissonConfigImpl(RedissonClient redissonClient, String keyPrefix);
// Redisson operations with distributed locks
@Override
public Lock getConfigLock(String key);
@Override
public String getConfigValue(String key, String defaultValue);
@Override
public void setConfigValue(String key, String value);
}
// Enhanced Redis Configuration
public class WxMaRedisBetterConfigImpl extends AbstractWxMaRedisConfig {
private final RedisTemplate<String, String> redisTemplate;
private final StringRedisTemplate stringRedisTemplate;
public WxMaRedisBetterConfigImpl(RedisTemplate<String, String> redisTemplate,
StringRedisTemplate stringRedisTemplate);
// Enhanced Redis operations with better performance
@Override
public void updateAccessToken(String accessToken, int expiresInSeconds);
@Override
public boolean isAccessTokenExpired();
}
// Redis Connection Configuration
public class WxMaRedisConnectionConfigImpl extends AbstractWxMaRedisConfig {
private final RedisConnectionFactory connectionFactory;
public WxMaRedisConnectionConfigImpl(RedisConnectionFactory connectionFactory);
public WxMaRedisConnectionConfigImpl(RedisConnectionFactory connectionFactory, String keyPrefix);
// Direct Redis connection operations
}Base class providing common Redis configuration functionality.
public abstract class AbstractWxMaRedisConfig implements WxMaConfig {
protected String keyPrefix = "wx:ma:"; // Redis key prefix
// Configuration Keys
protected static final String ACCESS_TOKEN_KEY = "access_token";
protected static final String JSAPI_TICKET_KEY = "jsapi_ticket";
protected static final String CARD_API_TICKET_KEY = "card_api_ticket";
// Lock Keys
protected static final String ACCESS_TOKEN_LOCK_KEY = "access_token:lock";
protected static final String JSAPI_TICKET_LOCK_KEY = "jsapi_ticket:lock";
protected static final String CARD_API_TICKET_LOCK_KEY = "card_api_ticket:lock";
// Abstract methods to be implemented by subclasses
protected abstract String getValue(String key);
protected abstract void setValue(String key, String value, int expireSeconds);
protected abstract void expire(String key, int seconds);
protected abstract Long getExpire(String key);
protected abstract Lock getLock(String key);
// Common Redis operations
protected String buildKey(String key) {
return keyPrefix + getAppid() + ":" + key;
}
// Token management using Redis
@Override
public String getAccessToken() {
return getValue(buildKey(ACCESS_TOKEN_KEY));
}
@Override
public boolean isAccessTokenExpired() {
Long expire = getExpire(buildKey(ACCESS_TOKEN_KEY));
return expire == null || expire < 200; // 200 second buffer
}
@Override
public void updateAccessToken(String accessToken, int expiresInSeconds) {
setValue(buildKey(ACCESS_TOKEN_KEY), accessToken, expiresInSeconds - 200);
}
@Override
public Lock getAccessTokenLock() {
return getLock(buildKey(ACCESS_TOKEN_LOCK_KEY));
}
}@Configuration
public class WeChatMiniAppConfig {
@Value("${wechat.miniapp.appid}")
private String appid;
@Value("${wechat.miniapp.secret}")
private String secret;
@Value("${wechat.miniapp.token:}")
private String token;
@Value("${wechat.miniapp.aes-key:}")
private String aesKey;
@Bean
public WxMaConfig wxMaConfig() {
WxMaDefaultConfigImpl config = new WxMaDefaultConfigImpl();
config.setAppid(appid);
config.setSecret(secret);
config.setToken(token);
config.setAesKey(aesKey);
// HTTP configuration
config.setRetrySleepMillis(1000);
config.setMaxRetryTimes(3);
// Enable stable access token (recommended for production)
config.useStableAccessToken(true);
return config;
}
@Bean
public WxMaService wxMaService(WxMaConfig wxMaConfig) {
WxMaServiceImpl service = new WxMaServiceImpl();
service.setWxMaConfig(wxMaConfig);
return service;
}
}@Configuration
@ConditionalOnProperty(name = "wechat.redis.enabled", havingValue = "true")
public class WeChatRedisConfig {
@Autowired
private RedisTemplate<String, String> redisTemplate;
@Bean
public WxMaConfig wxMaRedisConfig() {
WxMaRedisConfigImpl config = new WxMaRedisConfigImpl(redisTemplate, "wechat:ma:");
config.setAppid("your-appid");
config.setSecret("your-secret");
config.setToken("your-token");
config.setAesKey("your-aes-key");
// Configure HTTP settings
config.setHttpProxyHost("proxy.company.com");
config.setHttpProxyPort(8080);
config.setHttpProxyUsername("proxy-user");
config.setHttpProxyPassword("proxy-pass");
// Configure retry settings
config.setRetrySleepMillis(2000);
config.setMaxRetryTimes(5);
return config;
}
}@Configuration
public class MultiAppWeChatConfig {
@Bean
public WxMaService multiAppWxMaService() {
WxMaServiceImpl service = new WxMaServiceImpl();
// Configure multiple apps
Map<String, WxMaConfig> configs = new HashMap<>();
// App 1 - Main app
WxMaDefaultConfigImpl config1 = new WxMaDefaultConfigImpl();
config1.setAppid("app1-appid");
config1.setSecret("app1-secret");
config1.setToken("app1-token");
configs.put("main", config1);
// App 2 - Secondary app
WxMaDefaultConfigImpl config2 = new WxMaDefaultConfigImpl();
config2.setAppid("app2-appid");
config2.setSecret("app2-secret");
config2.setToken("app2-token");
configs.put("secondary", config2);
// App 3 - Test environment
WxMaDefaultConfigImpl config3 = new WxMaDefaultConfigImpl();
config3.setAppid("test-appid");
config3.setSecret("test-secret");
config3.setToken("test-token");
configs.put("test", config3);
service.setMultiConfigs(configs, "main"); // "main" as default
return service;
}
}
@Service
public class MultiAppService {
@Autowired
private WxMaService wxMaService;
public void handleMainAppRequest(String jsCode) {
// Use main app (default)
WxMaJscode2SessionResult session = wxMaService.getUserService()
.getSessionInfo(jsCode);
// Process main app request...
}
public void handleSecondaryAppRequest(String jsCode) {
// Switch to secondary app
wxMaService.switchoverTo("secondary");
try {
WxMaJscode2SessionResult session = wxMaService.getUserService()
.getSessionInfo(jsCode);
// Process secondary app request...
} finally {
// Switch back to default
wxMaService.switchoverTo("main");
}
}
public void handleTestRequest(String jsCode) {
// Use test environment
wxMaService.switchoverTo("test");
try {
WxMaJscode2SessionResult session = wxMaService.getUserService()
.getSessionInfo(jsCode);
// Process test request...
} finally {
wxMaService.switchoverTo("main");
}
}
}@Configuration
public class CustomHttpClientConfig {
@Bean
public WxMaConfig customHttpConfig() {
WxMaDefaultConfigImpl config = new WxMaDefaultConfigImpl();
config.setAppid("your-appid");
config.setSecret("your-secret");
// Custom Apache HttpClient configuration
ApacheHttpClientBuilder httpClientBuilder = ApacheHttpClientBuilder.create()
.setConnectionTimeout(10000) // 10 seconds connection timeout
.setSocketTimeout(30000) // 30 seconds socket timeout
.setConnectionRequestTimeout(5000) // 5 seconds connection request timeout
.setMaxTotalConnections(200) // Max total connections
.setMaxConnectionsPerRoute(50) // Max connections per route
.setRetryCount(3); // Retry count
// Custom connection pool configuration
httpClientBuilder.setConnectionTimeToLive(300, TimeUnit.SECONDS);
httpClientBuilder.setConnectionKeepAlive(true);
config.setApacheHttpClientBuilder(httpClientBuilder);
// Configure retry behavior
config.setRetrySleepMillis(1500);
config.setMaxRetryTimes(3);
return config;
}
}@Component
public class TokenManagementService {
@Bean
public WxMaConfig tokenCallbackConfig() {
WxMaDefaultConfigImpl config = new WxMaDefaultConfigImpl();
config.setAppid("your-appid");
config.setSecret("your-secret");
// Enable token update callbacks
config.enableUpdateAccessTokenBefore(true);
// Set token update callback
config.setUpdateAccessTokenBefore(tokenEntity -> {
logger.info("Access token about to be updated: {} -> {}",
tokenEntity.getOldToken(), tokenEntity.getNewToken());
// Store token update in audit log
auditService.logTokenUpdate(tokenEntity);
// Update token in external cache if needed
externalCacheService.updateToken(tokenEntity.getNewToken());
// Notify monitoring system
monitoringService.notifyTokenUpdate(tokenEntity);
});
return config;
}
@Autowired
private AuditService auditService;
@Autowired
private ExternalCacheService externalCacheService;
@Autowired
private MonitoringService monitoringService;
}@Configuration
@Profile("production")
public class ProductionRedisConfig {
@Bean
public LettuceConnectionFactory redisConnectionFactory() {
RedisClusterConfiguration clusterConfig = new RedisClusterConfiguration(
Arrays.asList(
"redis-cluster-node1:7000",
"redis-cluster-node2:7000",
"redis-cluster-node3:7000"
)
);
clusterConfig.setPassword("redis-password");
LettuceClientConfiguration clientConfig = LettuceClientConfiguration.builder()
.commandTimeout(Duration.ofSeconds(2))
.shutdownTimeout(Duration.ZERO)
.build();
return new LettuceConnectionFactory(clusterConfig, clientConfig);
}
@Bean
public WxMaConfig productionWxMaConfig(RedisConnectionFactory connectionFactory) {
WxMaRedisConnectionConfigImpl config = new WxMaRedisConnectionConfigImpl(
connectionFactory, "prod:wechat:ma:"
);
config.setAppid("prod-appid");
config.setSecret("prod-secret");
config.setToken("prod-token");
config.setAesKey("prod-aes-key");
// Production settings
config.useStableAccessToken(true);
config.setRetrySleepMillis(2000);
config.setMaxRetryTimes(5);
// Configure for high availability
config.setHttpProxyHost(null); // No proxy in production
config.setMaxRetryTimes(10); // More retries for reliability
return config;
}
}@Configuration
public class EnvironmentAwareConfig {
@Value("${spring.profiles.active:dev}")
private String activeProfile;
@Bean
@ConditionalOnProperty(name = "spring.profiles.active", havingValue = "dev")
public WxMaConfig devConfig() {
WxMaDefaultConfigImpl config = new WxMaDefaultConfigImpl();
config.setAppid("dev-appid");
config.setSecret("dev-secret");
config.setRetrySleepMillis(500); // Faster retries in dev
config.setMaxRetryTimes(2); // Fewer retries in dev
return config;
}
@Bean
@ConditionalOnProperty(name = "spring.profiles.active", havingValue = "staging")
public WxMaConfig stagingConfig(RedisTemplate<String, String> redisTemplate) {
WxMaRedisConfigImpl config = new WxMaRedisConfigImpl(
redisTemplate, "staging:wechat:ma:"
);
config.setAppid("staging-appid");
config.setSecret("staging-secret");
config.useStableAccessToken(true);
return config;
}
@Bean
@ConditionalOnProperty(name = "spring.profiles.active", havingValue = "prod")
public WxMaConfig productionConfig(RedissonClient redissonClient) {
WxMaRedissonConfigImpl config = new WxMaRedissonConfigImpl(
redissonClient, "prod:wechat:ma:"
);
config.setAppid("prod-appid");
config.setSecret("prod-secret");
config.useStableAccessToken(true);
config.setRetrySleepMillis(3000);
config.setMaxRetryTimes(8);
return config;
}
}@Component
public class WeChatConfigHealthIndicator implements HealthIndicator {
@Autowired
private WxMaService wxMaService;
@Override
public Health health() {
try {
WxMaConfig config = wxMaService.getWxMaConfig();
// Check basic configuration
if (config.getAppid() == null || config.getSecret() == null) {
return Health.down()
.withDetail("error", "Missing required configuration")
.build();
}
// Check access token
String accessToken = config.getAccessToken();
boolean tokenExpired = config.isAccessTokenExpired();
Health.Builder builder = Health.up()
.withDetail("appid", config.getAppid())
.withDetail("hasAccessToken", accessToken != null)
.withDetail("tokenExpired", tokenExpired)
.withDetail("configType", config.getClass().getSimpleName());
// Check Redis connection if using Redis config
if (config instanceof AbstractWxMaRedisConfig) {
builder.withDetail("storageType", "Redis");
} else {
builder.withDetail("storageType", "Memory");
}
return builder.build();
} catch (Exception e) {
return Health.down()
.withDetail("error", e.getMessage())
.build();
}
}
}
@Component
public class ConfigurationMetrics {
@Autowired
private WxMaService wxMaService;
@Autowired
private MeterRegistry meterRegistry;
@PostConstruct
public void initMetrics() {
// Track access token age
Gauge.builder("wechat.access_token.age.seconds")
.description("Age of current access token in seconds")
.register(meterRegistry, this, metrics -> {
try {
WxMaConfig config = wxMaService.getWxMaConfig();
// Calculate token age based on expiration
return config.isAccessTokenExpired() ? -1 :
calculateTokenAge(config.getAccessToken());
} catch (Exception e) {
return -1;
}
});
}
@EventListener
public void onAccessTokenUpdate(AccessTokenUpdateEvent event) {
meterRegistry.counter("wechat.access_token.updates.total")
.increment();
}
@Scheduled(fixedRate = 60000) // Every minute
public void recordConfigMetrics() {
try {
WxMaConfig config = wxMaService.getWxMaConfig();
// Record token expiration status
meterRegistry.gauge("wechat.access_token.expired",
config.isAccessTokenExpired() ? 1 : 0);
// Record JSAPI ticket status
meterRegistry.gauge("wechat.jsapi_ticket.expired",
config.isJsapiTicketExpired() ? 1 : 0);
} catch (Exception e) {
logger.error("Failed to record config metrics: {}", e.getMessage());
}
}
}@Configuration
public class SecureConfigurationExample {
// Use environment variables for sensitive data
@Value("${WECHAT_APPID}")
private String appid;
@Value("${WECHAT_SECRET}")
private String secret;
@Value("${WECHAT_AES_KEY}")
private String aesKey;
@Bean
public WxMaConfig secureConfig() {
WxMaDefaultConfigImpl config = new WxMaDefaultConfigImpl();
config.setAppid(appid);
config.setSecret(secret);
config.setAesKey(aesKey);
// Configure API signature for enhanced security
config.setApiSignatureRsaPrivateKey(loadRsaPrivateKey());
config.setApiSignatureAesKey(loadApiAesKey());
return config;
}
private String loadRsaPrivateKey() {
try {
// Load from secure key management system
return keyManagementService.getPrivateKey("wechat-rsa");
} catch (Exception e) {
logger.error("Failed to load RSA private key: {}", e.getMessage());
throw new ConfigurationException("Security key loading failed", e);
}
}
private String loadApiAesKey() {
try {
return keyManagementService.getAesKey("wechat-api");
} catch (Exception e) {
logger.error("Failed to load API AES key: {}", e.getMessage());
throw new ConfigurationException("Security key loading failed", e);
}
}
@Autowired
private KeyManagementService keyManagementService;
}This configuration module provides comprehensive setup options for WeChat MiniApp applications, supporting both development and production environments with proper security, monitoring, and scalability considerations.
Install with Tessl CLI
npx tessl i tessl/maven-com-github-binarywang--weixin-java-miniapp