p7b.....
This commit is contained in:
1 parent
12bcc0aa69
commit
d4bb3c446e
3 files changed
+390
-3
No files matched your search
@@ -0,0 +1,191 @@
|
|||||||
|
# Square Webhook Setup - Quick Reference
|
||||||
|
|
||||||
|
## Complete Setup Process
|
||||||
|
|
||||||
|
### Step 1: Generate SSL Certificate
|
||||||
|
```powershell
|
||||||
|
# Generate PEM certificate
|
||||||
|
python generate_cert.py
|
||||||
|
# Creates: cert.pem, key.pem
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 2: Convert to P7B for Square
|
||||||
|
```powershell
|
||||||
|
# Convert PEM to P7B (PKCS#7) format
|
||||||
|
python convert_to_p7b.py
|
||||||
|
# Creates: cert.p7b
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 3: Start HTTPS Server
|
||||||
|
```powershell
|
||||||
|
# Start webhook server with HTTPS
|
||||||
|
python src/test_server.py --ssl-cert cert.pem --ssl-key key.pem --hook-port 8443 --hook-ssl-only
|
||||||
|
|
||||||
|
# Server URLs:
|
||||||
|
# Main: http://localhost:7432/
|
||||||
|
# Webhook: https://localhost:8443/hook (or https://yourdomain.com:8443/hook)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 4: Configure Square Webhook
|
||||||
|
|
||||||
|
1. **Upload P7B Certificate to Square:**
|
||||||
|
- Go to: https://developer.squareup.com/apps
|
||||||
|
- Select your application
|
||||||
|
- Navigate to: **Webhooks** → **Manage Notification Settings**
|
||||||
|
- Upload `cert.p7b` file
|
||||||
|
- This allows Square to verify webhook signatures
|
||||||
|
|
||||||
|
2. **Add Webhook Subscription:**
|
||||||
|
- Still in Webhooks section
|
||||||
|
- Click **"Add Subscription"**
|
||||||
|
- Enter URL: `https://yourdomain.com:8443/hook`
|
||||||
|
- Select events to subscribe to (e.g., `inventory.count.updated`)
|
||||||
|
- Click **Save**
|
||||||
|
|
||||||
|
### Step 5: Test the Webhook
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Test from command line
|
||||||
|
python src/test_webhook.py
|
||||||
|
|
||||||
|
# Or manually with curl
|
||||||
|
curl -X POST https://localhost:8443/hook \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"type":"inventory.count.updated","merchant_id":"test"}'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Production Deployment
|
||||||
|
|
||||||
|
### Using Let's Encrypt (Recommended)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Get Let's Encrypt certificate (Linux/WSL)
|
||||||
|
sudo certbot certonly --standalone -d yourdomain.com
|
||||||
|
|
||||||
|
# 2. Convert to P7B for Square
|
||||||
|
python convert_to_p7b.py \
|
||||||
|
--cert /etc/letsencrypt/live/yourdomain.com/fullchain.pem \
|
||||||
|
--output square.p7b
|
||||||
|
|
||||||
|
# 3. Upload square.p7b to Square Dashboard
|
||||||
|
|
||||||
|
# 4. Run server
|
||||||
|
python src/test_server.py \
|
||||||
|
--ssl-cert /etc/letsencrypt/live/yourdomain.com/fullchain.pem \
|
||||||
|
--ssl-key /etc/letsencrypt/live/yourdomain.com/privkey.pem \
|
||||||
|
--host 0.0.0.0 \
|
||||||
|
--hook-port 443 \
|
||||||
|
--hook-ssl-only
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using ngrok (Easy Testing)
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Terminal 1: Start your server (HTTP is fine)
|
||||||
|
python src/test_server.py --hook-port 8080
|
||||||
|
|
||||||
|
# Terminal 2: Expose with ngrok (provides HTTPS automatically)
|
||||||
|
ngrok http 8080
|
||||||
|
|
||||||
|
# Use the ngrok HTTPS URL in Square:
|
||||||
|
# Example: https://abc123.ngrok.io/hook
|
||||||
|
|
||||||
|
# For P7B certificate with ngrok:
|
||||||
|
# ngrok provides its own certificate, so you don't need to upload one to Square
|
||||||
|
```
|
||||||
|
|
||||||
|
## Webhook Event Types
|
||||||
|
|
||||||
|
Common Square webhook events you can subscribe to:
|
||||||
|
|
||||||
|
- `inventory.count.updated` - Inventory quantity changes
|
||||||
|
- `catalog.version.updated` - Catalog item updates
|
||||||
|
- `payment.created` - New payment received
|
||||||
|
- `payment.updated` - Payment status changes
|
||||||
|
- `order.created` - New order created
|
||||||
|
- `order.updated` - Order status changes
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Certificate Upload Fails
|
||||||
|
- Ensure file is in P7B format (not PEM)
|
||||||
|
- File must be valid PKCS#7 structure
|
||||||
|
- Check file isn't corrupted: `openssl pkcs7 -print_certs -in cert.p7b`
|
||||||
|
|
||||||
|
### Webhook Not Receiving Data
|
||||||
|
1. Check server is running and accessible from internet
|
||||||
|
2. Verify URL in Square Dashboard is correct
|
||||||
|
3. Check firewall allows incoming traffic on webhook port
|
||||||
|
4. Review server logs: `./logs/studspider.log`
|
||||||
|
|
||||||
|
### SSL Certificate Errors
|
||||||
|
- Self-signed certificates won't work with Square in production
|
||||||
|
- Use Let's Encrypt or commercial CA for production
|
||||||
|
- Use ngrok for quick testing (provides valid HTTPS)
|
||||||
|
|
||||||
|
### Testing Locally
|
||||||
|
```powershell
|
||||||
|
# Square can't reach localhost - use one of these:
|
||||||
|
# 1. ngrok (easiest)
|
||||||
|
ngrok http 8443
|
||||||
|
|
||||||
|
# 2. Cloudflare Tunnel
|
||||||
|
cloudflared tunnel --url http://localhost:8443
|
||||||
|
|
||||||
|
# 3. Deploy to cloud (AWS, Azure, DigitalOcean, etc.)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Verifying Webhook Signatures
|
||||||
|
|
||||||
|
Square signs webhook requests. Your server should verify signatures:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# TODO: Add to hook_handler in test_server.py
|
||||||
|
import hmac
|
||||||
|
import hashlib
|
||||||
|
|
||||||
|
def verify_square_signature(payload: bytes, signature: str, webhook_signature_key: str) -> bool:
|
||||||
|
"""Verify Square webhook signature"""
|
||||||
|
computed_signature = hmac.new(
|
||||||
|
webhook_signature_key.encode('utf-8'),
|
||||||
|
payload,
|
||||||
|
hashlib.sha256
|
||||||
|
).digest()
|
||||||
|
|
||||||
|
expected_signature = base64.b64decode(signature)
|
||||||
|
return hmac.compare_digest(computed_signature, expected_signature)
|
||||||
|
```
|
||||||
|
|
||||||
|
Get your webhook signature key from Square Dashboard → Webhooks → Signature Key.
|
||||||
|
|
||||||
|
## Environment Variables
|
||||||
|
|
||||||
|
Add to your `.env` file:
|
||||||
|
|
||||||
|
```env
|
||||||
|
# Square Webhook Configuration
|
||||||
|
SQUARE_WEBHOOK_SIGNATURE_KEY=your_signature_key_from_square_dashboard
|
||||||
|
|
||||||
|
# SSL Certificate Paths (production)
|
||||||
|
SSL_CERT_PATH=/path/to/fullchain.pem
|
||||||
|
SSL_KEY_PATH=/path/to/privkey.pem
|
||||||
|
```
|
||||||
|
|
||||||
|
## Commands Quick Reference
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Generate certificate
|
||||||
|
python generate_cert.py
|
||||||
|
|
||||||
|
# Convert to P7B
|
||||||
|
python convert_to_p7b.py
|
||||||
|
|
||||||
|
# Start HTTPS server
|
||||||
|
python src/test_server.py --ssl-cert cert.pem --ssl-key key.pem --hook-port 8443 --hook-ssl-only
|
||||||
|
|
||||||
|
# Test webhook
|
||||||
|
python src/test_webhook.py
|
||||||
|
|
||||||
|
# Start with ngrok
|
||||||
|
ngrok http 8443
|
||||||
|
```
|
||||||
@@ -0,0 +1,166 @@
|
|||||||
|
"""
|
||||||
|
Convert PEM certificate to P7B (PKCS#7) format for Square webhooks
|
||||||
|
Square requires P7B format for webhook signature verification
|
||||||
|
"""
|
||||||
|
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
def convert_pem_to_p7b(cert_file="cert.pem", output_file="cert.p7b"):
|
||||||
|
"""
|
||||||
|
Convert PEM certificate to P7B (PKCS#7) format
|
||||||
|
|
||||||
|
Args:
|
||||||
|
cert_file: Input PEM certificate file
|
||||||
|
output_file: Output P7B file
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Check if OpenSSL is available
|
||||||
|
try:
|
||||||
|
subprocess.run(["openssl", "version"], capture_output=True, check=True)
|
||||||
|
except (subprocess.CalledProcessError, FileNotFoundError):
|
||||||
|
print("ERROR: OpenSSL not found!")
|
||||||
|
print("\nFor Windows, you can:")
|
||||||
|
print(
|
||||||
|
"1. Install OpenSSL from: https://slproweb.com/products/Win32OpenSSL.html"
|
||||||
|
)
|
||||||
|
print("2. Or use Git Bash which includes OpenSSL")
|
||||||
|
print("3. Or use WSL (Windows Subsystem for Linux)")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Check if input file exists
|
||||||
|
if not os.path.exists(cert_file):
|
||||||
|
print(f"ERROR: Certificate file not found: {cert_file}")
|
||||||
|
print(f"Current directory: {os.getcwd()}")
|
||||||
|
print("\nMake sure you've generated the certificate first:")
|
||||||
|
print(" python generate_cert.py")
|
||||||
|
return False
|
||||||
|
|
||||||
|
print(f"Converting {cert_file} to P7B format...")
|
||||||
|
|
||||||
|
# Convert PEM to P7B (PKCS#7)
|
||||||
|
# -certfile can include intermediate certificates if needed
|
||||||
|
cmd = [
|
||||||
|
"openssl",
|
||||||
|
"crl2pkcs7",
|
||||||
|
"-nocrl",
|
||||||
|
"-certfile",
|
||||||
|
cert_file,
|
||||||
|
"-out",
|
||||||
|
output_file,
|
||||||
|
]
|
||||||
|
|
||||||
|
try:
|
||||||
|
result = subprocess.run(cmd, check=True, capture_output=True, text=True)
|
||||||
|
|
||||||
|
if os.path.exists(output_file):
|
||||||
|
print(f"\n✓ P7B certificate created: {os.path.abspath(output_file)}")
|
||||||
|
print(f" File size: {os.path.getsize(output_file)} bytes")
|
||||||
|
|
||||||
|
print("\n" + "=" * 60)
|
||||||
|
print("Square Webhook Configuration:")
|
||||||
|
print("=" * 60)
|
||||||
|
print(f"1. Upload this file to Square Developer Dashboard:")
|
||||||
|
print(f" {os.path.abspath(output_file)}")
|
||||||
|
print("\n2. In Square Dashboard:")
|
||||||
|
print(" - Go to Webhooks settings")
|
||||||
|
print(" - Click 'Manage Notification Settings'")
|
||||||
|
print(" - Upload the P7B file for signature verification")
|
||||||
|
print("\n3. Square will use this to verify webhook signatures")
|
||||||
|
print("=" * 60)
|
||||||
|
|
||||||
|
# Show file contents preview
|
||||||
|
print("\nP7B file preview (first 500 chars):")
|
||||||
|
with open(output_file, "r") as f:
|
||||||
|
content = f.read(500)
|
||||||
|
print(content)
|
||||||
|
if len(content) == 500:
|
||||||
|
print("...")
|
||||||
|
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
print("ERROR: Output file was not created")
|
||||||
|
return False
|
||||||
|
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
print(f"ERROR: Conversion failed: {e}")
|
||||||
|
if e.stderr:
|
||||||
|
print(f"OpenSSL error: {e.stderr}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def convert_with_chain(cert_file="cert.pem", chain_file=None, output_file="cert.p7b"):
|
||||||
|
"""
|
||||||
|
Convert PEM certificate with certificate chain to P7B format
|
||||||
|
Use this if you have intermediate certificates
|
||||||
|
|
||||||
|
Args:
|
||||||
|
cert_file: Your certificate file
|
||||||
|
chain_file: Intermediate/chain certificate file (optional)
|
||||||
|
output_file: Output P7B file
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not chain_file:
|
||||||
|
return convert_pem_to_p7b(cert_file, output_file)
|
||||||
|
|
||||||
|
# Check files exist
|
||||||
|
for f in [cert_file, chain_file]:
|
||||||
|
if not os.path.exists(f):
|
||||||
|
print(f"ERROR: File not found: {f}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
print(f"Converting {cert_file} with chain {chain_file} to P7B format...")
|
||||||
|
|
||||||
|
# Create combined cert file
|
||||||
|
combined = "combined_cert.pem"
|
||||||
|
with open(combined, "w") as out:
|
||||||
|
with open(cert_file, "r") as f:
|
||||||
|
out.write(f.read())
|
||||||
|
with open(chain_file, "r") as f:
|
||||||
|
out.write(f.read())
|
||||||
|
|
||||||
|
result = convert_pem_to_p7b(combined, output_file)
|
||||||
|
|
||||||
|
# Clean up combined file
|
||||||
|
if os.path.exists(combined):
|
||||||
|
os.remove(combined)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description="Convert PEM certificate to P7B (PKCS#7) for Square webhooks"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--cert",
|
||||||
|
default="cert.pem",
|
||||||
|
help="Input PEM certificate file (default: cert.pem)",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--chain", default=None, help="Optional: Intermediate certificate chain file"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--output", default="cert.p7b", help="Output P7B file (default: cert.p7b)"
|
||||||
|
)
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
print("=" * 60)
|
||||||
|
print("PEM to P7B Certificate Converter for Square Webhooks")
|
||||||
|
print("=" * 60)
|
||||||
|
print()
|
||||||
|
|
||||||
|
if args.chain:
|
||||||
|
success = convert_with_chain(args.cert, args.chain, args.output)
|
||||||
|
else:
|
||||||
|
success = convert_pem_to_p7b(args.cert, args.output)
|
||||||
|
|
||||||
|
if not success:
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
print("\n✓ Conversion complete!")
|
||||||
+33
-3
@@ -267,12 +267,42 @@ if __name__ == "__main__":
|
|||||||
# Setup SSL context if certificates provided
|
# Setup SSL context if certificates provided
|
||||||
ssl_context = None
|
ssl_context = None
|
||||||
if args.ssl_cert and args.ssl_key:
|
if args.ssl_cert and args.ssl_key:
|
||||||
ssl_context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
|
# Check if trying to use P7B file
|
||||||
ssl_context.load_cert_chain(args.ssl_cert, args.ssl_key)
|
if args.ssl_cert.endswith(".p7b") or args.ssl_key.endswith(".p7b"):
|
||||||
print(f"SSL enabled with cert: {args.ssl_cert}")
|
print("ERROR: Cannot use P7B files directly for HTTPS server!")
|
||||||
|
print("\nP7B files are for Square's webhook signature verification only.")
|
||||||
|
print("Your server needs PEM format files (.pem)")
|
||||||
|
print("\nWorkflow:")
|
||||||
|
print("1. Generate PEM files: python generate_cert.py")
|
||||||
|
print("2. Convert to P7B for Square: python convert_to_p7b.py")
|
||||||
|
print("3. Upload P7B to Square Dashboard")
|
||||||
|
print("4. Run server with PEM files:")
|
||||||
|
print(
|
||||||
|
" python src/test_server.py --ssl-cert cert.pem --ssl-key key.pem --hook-port 8443"
|
||||||
|
)
|
||||||
|
print("\nIf you have a P7B and need PEM:")
|
||||||
|
print(" python convert_from_p7b.py your_cert.p7b")
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
try:
|
||||||
|
ssl_context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
|
||||||
|
ssl_context.load_cert_chain(args.ssl_cert, args.ssl_key)
|
||||||
|
print(f"✓ SSL enabled with cert: {args.ssl_cert}")
|
||||||
|
except FileNotFoundError as e:
|
||||||
|
print(f"ERROR: Certificate file not found: {e}")
|
||||||
|
exit(1)
|
||||||
|
except ssl.SSLError as e:
|
||||||
|
print(f"ERROR: Invalid certificate or key file: {e}")
|
||||||
|
print("\nMake sure you're using PEM format files.")
|
||||||
|
print("Generate certificates with: python generate_cert.py")
|
||||||
|
exit(1)
|
||||||
elif args.ssl_cert or args.ssl_key:
|
elif args.ssl_cert or args.ssl_key:
|
||||||
print("ERROR: Both --ssl-cert and --ssl-key must be provided together")
|
print("ERROR: Both --ssl-cert and --ssl-key must be provided together")
|
||||||
exit(1)
|
exit(1)
|
||||||
|
else:
|
||||||
|
print("ℹ️ Running without SSL (HTTP only)")
|
||||||
|
print(" For production with nginx reverse proxy, this is recommended.")
|
||||||
|
print(" Nginx will handle HTTPS, forward HTTP to this server.")
|
||||||
|
|
||||||
# If hook-port is specified, run a separate server for webhooks
|
# If hook-port is specified, run a separate server for webhooks
|
||||||
if args.hook_port:
|
if args.hook_port:
|
||||||
|
|||||||
Reference in new issue
Block a user