A production-ready Python service for handling Apple App Store Server Notifications (v2) for subscription events. This service provides a webhook endpoint for receiving Apple's server notifications, processes them, and provides REST APIs for querying user subscription statuses.
If you're setting up on a newly reinstalled VPS, follow these steps for a complete installation:
-
Connect to your VPS:
ssh user@your-vps-ip
-
Update your system:
sudo apt update && sudo apt upgrade -y -
Install Git:
sudo apt install git -y
-
Clone the repository:
git clone https://github.com/TeccClubb/Apple-Webhook.git cd Apple-Webhook -
Make the deployment scripts executable:
chmod +x deploy.sh troubleshoot.sh check_system.py
-
Run the system compatibility checker to automatically install required dependencies:
sudo python3 check_system.py --fix
-
Deploy the service:
sudo ./deploy.sh
-
If you encounter any dependency issues, especially with PostgreSQL or psycopg2, run the troubleshooting script:
sudo ./troubleshoot.sh
-
Copy your Apple private key to the server:
# On your local machine: scp /path/to/AuthKey_XXXXX.p8 user@your-vps-ip:~/Apple-Webhook/keys/ # On your VPS: sudo chmod 600 ~/Apple-Webhook/keys/AuthKey_XXXXX.p8 sudo chown appuser:appuser ~/Apple-Webhook/keys/AuthKey_XXXXX.p8
-
Verify the deployment:
curl https://apple.safeprovpn.com/api/v1/test-connection
For easy deployment to your VPS with domain apple.safeprovpn.com, use the included deployment script:
-
Connect to your VPS:
ssh user@your-vps-ip
-
Upload the deploy.sh script to your server:
scp deploy.sh user@your-vps-ip:~/ -
Make the script executable and run it:
chmod +x deploy.sh sudo ./deploy.sh
This script will:
- Install all dependencies (Python, Nginx, PostgreSQL, etc.)
- Set up a PostgreSQL database
- Configure the environment with your Apple credentials
- Set up HTTPS with Let's Encrypt
- Configure Nginx as a reverse proxy
- Start the service using Supervisor
After deployment, test your connection to Apple's servers:
curl https://apple.safeprovpn.com/api/v1/test-connection- Process Apple App Store Server Notifications via webhook
- Verify Apple's JWS signatures for security
- Store subscription data in a database
- Provide REST APIs to query user subscription status
- Process all App Store notification types (SUBSCRIBED, DID_RENEW, EXPIRED, etc.)
- Authentication for API endpoints
- Proper error handling and logging
- FastAPI: Modern, high-performance web framework for API development
- SQLAlchemy: SQL toolkit and ORM
- Pydantic: Data validation and settings management
- Python-dotenv: Environment configuration
- JWT: Authentication and Apple JWS verification
POST /api/v1/webhook/apple: Receives and processes Apple App Store Server NotificationsGET /api/v1/subscriptions/status/{user_id}: Checks a user's subscription statusGET /api/v1/subscriptions/active/{user_id}: Gets a user's active subscriptionsPOST /api/v1/subscriptions/auth: Obtains API authentication tokens
- Python 3.8 or higher
- PostgreSQL (for production) or SQLite (for development)
- pip (Python package manager)
- Clone the repository:
git clone <repository-url>
cd apple-subscription-service- Create a virtual environment:
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate- Install dependencies:
pip install -r requirements.txt- Set up environment variables:
cp example.env .envEdit the .env file and set appropriate values for your environment.
- Run the development server:
uvicorn main:app --reload- Access the API documentation at:
http://localhost:8000/api/docs
- Install Python and required packages on your Linux server:
sudo apt update
sudo apt install python3 python3-pip python3-dev libpq-dev postgresql postgresql-contrib nginx- Clone the repository:
git clone <repository-url>
cd apple-subscription-service- Create a virtual environment:
python3 -m venv venv
source venv/bin/activate- Install dependencies:
pip install -r requirements.txt
pip install gunicorn- Set up environment variables:
cp example.env .envEdit the .env file with production values, particularly:
- Set
DEBUG=False - Set a strong
SECRET_KEY - Configure proper
DATABASE_URLfor PostgreSQL - Set appropriate
APPLE_*variables
- Create a PostgreSQL database:
sudo -u postgres psqlCREATE DATABASE apple_subscriptions;
CREATE USER app_user WITH PASSWORD 'secure_password';
GRANT ALL PRIVILEGES ON DATABASE apple_subscriptions TO app_user;- Update the
DATABASE_URLin your.envfile:
DATABASE_URL=postgresql://app_user:secure_password@localhost:5432/apple_subscriptions
- Create a systemd service file:
sudo nano /etc/systemd/system/apple-subscription.service- Add the following content:
[Unit]
Description=Apple Subscription Service
After=network.target
[Service]
User=ubuntu
Group=www-data
WorkingDirectory=/path/to/apple-subscription-service
Environment="PATH=/path/to/apple-subscription-service/venv/bin"
EnvironmentFile=/path/to/apple-subscription-service/.env
ExecStart=/path/to/apple-subscription-service/venv/bin/gunicorn -w 4 -k uvicorn.workers.UvicornWorker main:app --bind 0.0.0.0:8000
[Install]
WantedBy=multi-user.target
- Enable and start the service:
sudo systemctl enable apple-subscription
sudo systemctl start apple-subscription
sudo systemctl status apple-subscription- Create an Nginx site configuration:
sudo nano /etc/nginx/sites-available/apple-subscription- Add the following configuration:
server {
listen 80;
server_name your_domain.com;
location / {
proxy_pass http://localhost:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /api/docs {
proxy_pass http://localhost:8000/api/docs;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
- Create a symbolic link and test the configuration:
sudo ln -s /etc/nginx/sites-available/apple-subscription /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl restart nginx- Install Certbot:
sudo apt install certbot python3-certbot-nginx- Obtain and install a certificate:
sudo certbot --nginx -d your_domain.com-
Follow the prompts to complete the certificate installation.
-
Certbot will automatically update your Nginx configuration to use HTTPS.
-
Test the automatic renewal:
sudo certbot renew --dry-runTo access protected endpoints, obtain an authentication token:
POST /api/v1/subscriptions/auth
{
"username": "user@example.com",
"password": "password"
}
Use the returned token in the Authorization header:
Authorization: Bearer {token}
- Log in to App Store Connect
- Go to your app > App Information > App Store Server Notifications
- Set the Production URL to:
https://your_domain.com/api/v1/webhook/apple - Set the Sandbox URL to:
https://your_domain.com/api/v1/webhook/apple - Select Version 2 for the notification format
If you encounter issues with PostgreSQL or psycopg2-binary installation:
-
Install PostgreSQL development packages:
sudo apt-get update sudo apt-get install -y libpq-dev postgresql-server-dev-all build-essential python3-dev
-
Reinstall psycopg2 properly:
cd /opt/apple-subscription-service source venv/bin/activate pip install --no-cache-dir --no-build-isolation psycopg2-binary
-
If the above doesn't work, try the source version:
pip install --no-cache-dir psycopg2
If the application can't connect to the database:
-
Check PostgreSQL service status:
sudo systemctl status postgresql
-
Verify that PostgreSQL is configured to accept connections:
sudo nano /etc/postgresql/*/main/postgresql.conf # Ensure listen_addresses = 'localhost' is uncommented sudo nano /etc/postgresql/*/main/pg_hba.conf # Ensure there's a line like: "local all all peer"
-
Restart PostgreSQL after configuration changes:
sudo systemctl restart postgresql
If you can't access your API through HTTPS:
-
Check Nginx configuration:
sudo nginx -t
-
Verify that your domain points to your server:
nslookup apple.safeprovpn.com
-
Ensure Let's Encrypt certificates are properly set up:
sudo certbot certificates
If the service doesn't start:
-
Check the service status:
sudo supervisorctl status apple-subscription
-
Examine error logs:
tail -n 100 /opt/apple-subscription-service/logs/gunicorn-error.log
-
Run the application directly to see immediate output:
cd /opt/apple-subscription-service source venv/bin/activate python -m uvicorn main:app --host 127.0.0.1 --port 8080
View application logs:
sudo journalctl -u apple-subscriptionView supervisor logs:
sudo supervisorctl tail -f apple-subscriptionCommon commands for managing your service:
# Start the service
sudo supervisorctl start apple-subscription
# Stop the service
sudo supervisorctl stop apple-subscription
# Restart the service
sudo supervisorctl restart apple-subscription
# Check the status
sudo supervisorctl status apple-subscription
# View logs in real-time
sudo supervisorctl tail -f apple-subscriptionCommands for managing Nginx:
# Test configuration
sudo nginx -t
# Reload configuration without downtime
sudo systemctl reload nginx
# Restart Nginx
sudo systemctl restart nginx
# Check status
sudo systemctl status nginxCommands for managing PostgreSQL:
# Connect to PostgreSQL
sudo -u postgres psql
# Connect to the application database
sudo -u postgres psql -d apple_subscriptions
# Useful PostgreSQL commands:
# \dt - list tables
# \q - quit
# SELECT * FROM subscriptions LIMIT 10; - view recent subscriptions- Pull the latest code:
cd /opt/apple-subscription-service
sudo git pull- Install any new dependencies:
cd /opt/apple-subscription-service
sudo -u appuser bash -c "source venv/bin/activate && pip install -r requirements.txt"- Restart the service:
sudo supervisorctl restart apple-subscriptionBackup your PostgreSQL database:
# Create a backup
sudo -u postgres pg_dump apple_subscriptions > backup.sql
# Restore from backup
sudo -u postgres psql apple_subscriptions < backup.sql- Always use HTTPS in production
- Rotate the
SECRET_KEYperiodically - Keep dependencies updated
- Implement IP whitelisting for webhook endpoints if possible
- Monitor logs for unusual activity
- Important: Store private keys and secrets securely:
- Avoid committing private keys directly to repositories
- Consider using environment variables or secure secret management solutions
- If you've already committed sensitive files, consider rotating those credentials and using
.gitignore