Add Apple/Google IAP validation and subscription webhooks
- Add Apple App Store Server API integration for receipt/transaction validation - Add Google Play Developer API integration for purchase token validation - Add webhook endpoints for server-to-server subscription notifications - POST /api/subscription/webhook/apple/ (App Store Server Notifications v2) - POST /api/subscription/webhook/google/ (Real-time Developer Notifications) - Support both StoreKit 1 (receipt_data) and StoreKit 2 (transaction_id) - Add repository methods to find users by transaction ID or purchase token - Add configuration for IAP credentials (APPLE_IAP_*, GOOGLE_IAP_*) - Add setup documentation for configuring webhooks 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -23,6 +23,8 @@ type Config struct {
|
||||
Storage StorageConfig
|
||||
AppleAuth AppleAuthConfig
|
||||
GoogleAuth GoogleAuthConfig
|
||||
AppleIAP AppleIAPConfig
|
||||
GoogleIAP GoogleIAPConfig
|
||||
}
|
||||
|
||||
type ServerConfig struct {
|
||||
@@ -85,6 +87,21 @@ type GoogleAuthConfig struct {
|
||||
IOSClientID string // iOS client ID (optional, for audience verification)
|
||||
}
|
||||
|
||||
// AppleIAPConfig holds Apple App Store Server API configuration
|
||||
type AppleIAPConfig struct {
|
||||
KeyPath string // Path to .p8 private key file
|
||||
KeyID string // Key ID from App Store Connect
|
||||
IssuerID string // Issuer ID from App Store Connect
|
||||
BundleID string // App bundle ID (e.g., com.tt.casera)
|
||||
Sandbox bool // Use sandbox environment for testing
|
||||
}
|
||||
|
||||
// GoogleIAPConfig holds Google Play Developer API configuration
|
||||
type GoogleIAPConfig struct {
|
||||
ServiceAccountPath string // Path to service account JSON file
|
||||
PackageName string // Android package name (e.g., com.tt.casera)
|
||||
}
|
||||
|
||||
type WorkerConfig struct {
|
||||
// Scheduled job times (UTC)
|
||||
TaskReminderHour int
|
||||
@@ -206,6 +223,17 @@ func Load() (*Config, error) {
|
||||
AndroidClientID: viper.GetString("GOOGLE_ANDROID_CLIENT_ID"),
|
||||
IOSClientID: viper.GetString("GOOGLE_IOS_CLIENT_ID"),
|
||||
},
|
||||
AppleIAP: AppleIAPConfig{
|
||||
KeyPath: viper.GetString("APPLE_IAP_KEY_PATH"),
|
||||
KeyID: viper.GetString("APPLE_IAP_KEY_ID"),
|
||||
IssuerID: viper.GetString("APPLE_IAP_ISSUER_ID"),
|
||||
BundleID: viper.GetString("APPLE_IAP_BUNDLE_ID"),
|
||||
Sandbox: viper.GetBool("APPLE_IAP_SANDBOX"),
|
||||
},
|
||||
GoogleIAP: GoogleIAPConfig{
|
||||
ServiceAccountPath: viper.GetString("GOOGLE_IAP_SERVICE_ACCOUNT_PATH"),
|
||||
PackageName: viper.GetString("GOOGLE_IAP_PACKAGE_NAME"),
|
||||
},
|
||||
}
|
||||
|
||||
// Validate required fields
|
||||
@@ -267,6 +295,11 @@ func setDefaults() {
|
||||
viper.SetDefault("STORAGE_BASE_URL", "/uploads")
|
||||
viper.SetDefault("STORAGE_MAX_FILE_SIZE", 10*1024*1024) // 10MB
|
||||
viper.SetDefault("STORAGE_ALLOWED_TYPES", "image/jpeg,image/png,image/gif,image/webp,application/pdf")
|
||||
|
||||
// Apple IAP defaults
|
||||
viper.SetDefault("APPLE_IAP_SANDBOX", true) // Default to sandbox for safety
|
||||
|
||||
// Google IAP defaults - no defaults needed, will fail gracefully if not configured
|
||||
}
|
||||
|
||||
func validate(cfg *Config) error {
|
||||
|
||||
Reference in New Issue
Block a user