agentops/app/scripts/run-api-with-stripe.sh

210 lines
8.6 KiB
Bash

#!/bin/bash
# This script automates running the API with Stripe webhook listening.
# It fetches the webhook secret, starts 'stripe listen' in the background,
# and then runs the provided API command with the secret injected.
set -e # Exit immediately if a command exits with a non-zero status.
# --- Configuration ---
DEFAULT_API_PORT="8000" # Default API port if not provided as first argument
# Events Stripe CLI should listen for.
STRIPE_EVENTS="checkout.session.completed,subscription_schedule.canceled,subscription_schedule.completed,subscription_schedule.created,subscription_schedule.expiring,subscription_schedule.released"
# Command to run the API will be passed as arguments to this script after the optional port.
# --- Helper Functions ---
function check_command() {
if ! command -v "$1" &> /dev/null; then
echo "Error: Required command '$1' not found in PATH."
echo "Please install '$1' and ensure it's in your PATH."
exit 1
fi
}
STRIPE_LISTEN_PID=""
SECRET_OUTPUT_FILE=""
function cleanup() {
echo "Cleaning up... (called from LNO: ${BASH_LINENO[0]})"
set +x # Turn off command printing during cleanup to reduce noise
if [ -n "$STRIPE_LISTEN_PID" ] && ps -p "$STRIPE_LISTEN_PID" > /dev/null; then
echo "Stopping background Stripe listen process (PID: $STRIPE_LISTEN_PID)..."
kill "$STRIPE_LISTEN_PID"
wait "$STRIPE_LISTEN_PID" 2>/dev/null || true # Allow wait to not cause script exit
echo "Stripe listen process stopped."
elif [ -n "$STRIPE_LISTEN_PID" ]; then # If PID was set but process not found
echo "Background Stripe listen process (PID: $STRIPE_LISTEN_PID) was expected but not found or already stopped."
else # If PID was never set for persistent listener
echo "Background Stripe listen process was not started or its PID was not captured."
fi
if [ -n "$SECRET_OUTPUT_FILE" ] && [ -f "$SECRET_OUTPUT_FILE" ]; then
echo "Removing temporary secret file (if not preserved for error): $SECRET_OUTPUT_FILE"
# Check if we are exiting due to an error before removing, to preserve for inspection
# This is a simple check; a more robust way would be to pass exit code to cleanup.
# For now, if an error occurred in secret retrieval, it would have exited before successful rm.
rm -f "$SECRET_OUTPUT_FILE"
fi
}
# --- Main Script ---
# Trap exit signals to ensure cleanup
trap cleanup EXIT SIGINT SIGTERM
# Check for required tools
check_command "stripe"
check_command "mktemp"
check_command "grep"
check_command "sed"
# Determine API Port and API command
API_PORT="${1:-$DEFAULT_API_PORT}"
if [[ "$1" =~ ^[0-9]+$ ]]; then # if first arg is a port number
shift # remove port from arguments, rest are the API command
fi
if [ $# -eq 0 ]; then
echo "Error: No API command provided."
echo "Usage: $0 [API_PORT] <command_to_run_api...>"
echo "Example: $0 8000 docker compose up my-api-service"
exit 1
fi
API_RUN_COMMAND=("$@")
echo "Attempting to obtain Stripe webhook secret..."
echo "You may be prompted to authenticate with Stripe in your browser."
SECRET_OUTPUT_FILE=$(mktemp)
if [ -z "$SECRET_OUTPUT_FILE" ]; then
echo "Error: Could not create temporary file."
exit 1
fi
echo "Stripe CLI output for secret retrieval will be logged to: $SECRET_OUTPUT_FILE"
stripe listen --format JSON --forward-to "http://localhost:${API_PORT}/v4/stripe-webhook" --events "${STRIPE_EVENTS}" --skip-update > "${SECRET_OUTPUT_FILE}" 2>&1 &
STRIPE_SECRET_PID_TEMP=$!
echo "Started temporary Stripe listen for secret (PID: $STRIPE_SECRET_PID_TEMP). Waiting for secret..."
sleep 1
if ! ps -p "$STRIPE_SECRET_PID_TEMP" > /dev/null; then
echo "Error: Stripe listen process for secret retrieval (PID: $STRIPE_SECRET_PID_TEMP) seems to have died immediately after start."
echo "--- Output from Stripe CLI (from $SECRET_OUTPUT_FILE) --- BEGIN --- "
cat "${SECRET_OUTPUT_FILE}"
echo "--- Output from Stripe CLI (from $SECRET_OUTPUT_FILE) --- END --- "
exit 1
fi
timeout=300 # 5 minutes
elapsed=0
secret_found=false
WEBHOOK_SECRET=""
while [ $elapsed -lt $timeout ]; do
if ! ps -p "$STRIPE_SECRET_PID_TEMP" > /dev/null; then
echo "Error: Stripe listen process for secret retrieval (PID: $STRIPE_SECRET_PID_TEMP) died prematurely during wait loop."
echo "--- Output from Stripe CLI (from $SECRET_OUTPUT_FILE) --- BEGIN --- "
cat "${SECRET_OUTPUT_FILE}"
echo "--- Output from Stripe CLI (from $SECRET_OUTPUT_FILE) --- END --- "
exit 1
fi
if grep -q "Your webhook signing secret is" "${SECRET_OUTPUT_FILE}"; then
WEBHOOK_SECRET=$(grep "Your webhook signing secret is" "${SECRET_OUTPUT_FILE}" | sed -n 's/.*Your webhook signing secret is \(whsec_[^[:space:]]*\).*/\1/p' | tr -d '[:space:]')
if [ -n "$WEBHOOK_SECRET" ]; then
secret_found=true
echo "Stripe webhook secret identified from text output."
break
fi
fi
JQ_SECRET=$(jq -r '.secret' "${SECRET_OUTPUT_FILE}" 2>/dev/null)
if [ $? -eq 0 ] && [ -n "$JQ_SECRET" ] && [ "$JQ_SECRET" != "null" ]; then
WEBHOOK_SECRET=$JQ_SECRET
secret_found=true
echo "Stripe webhook secret identified from JSON output."
break
fi
sleep 1
elapsed=$((elapsed + 1))
if [ $((elapsed % 10)) -eq 0 ]; then
echo "Still waiting for Stripe secret... ($elapsed/$timeout seconds) (Temp PID: $STRIPE_SECRET_PID_TEMP is alive)"
fi
done
if ps -p "$STRIPE_SECRET_PID_TEMP" > /dev/null; then
echo "Stopping temporary Stripe listen for secret (PID: $STRIPE_SECRET_PID_TEMP)..."
kill "$STRIPE_SECRET_PID_TEMP"
wait "$STRIPE_SECRET_PID_TEMP" 2>/dev/null || true
echo "Temporary Stripe listen stopped."
fi
if [ "$secret_found" = false ]; then
echo "Error: Timed out waiting for Stripe webhook secret after $timeout seconds."
echo "--- Final output from Stripe CLI (from $SECRET_OUTPUT_FILE) --- BEGIN --- "
cat "${SECRET_OUTPUT_FILE}"
echo "--- Final output from Stripe CLI (from $SECRET_OUTPUT_FILE) --- END --- "
echo "!!! Preserving temporary secret file for inspection due to timeout: $SECRET_OUTPUT_FILE !!!"
exit 1
fi
if [ -z "$WEBHOOK_SECRET" ]; then
echo "Error: Logic failure - secret_found is true but WEBHOOK_SECRET is empty."
echo "!!! Preserving temporary secret file for inspection due to empty secret: $SECRET_OUTPUT_FILE !!!"
exit 1
fi
if [ -f "$SECRET_OUTPUT_FILE" ]; then
echo "Secret obtained, removing temporary file: $SECRET_OUTPUT_FILE"
rm -f "$SECRET_OUTPUT_FILE"
SECRET_OUTPUT_FILE=""
fi
echo "Stripe Webhook Secret obtained successfully: whsec_... (masked)"
# Now, start the actual 'stripe listen' in the background that will stay running
echo "Starting persistent background Stripe listen process..."
# Using --skip-update to prevent auto-updates.
stripe listen --forward-to "http://localhost:${API_PORT}/v4/stripe-webhook" --events "${STRIPE_EVENTS}" --skip-update &
STRIPE_LISTEN_PID=$!
echo "Persistent Stripe listen command launched. Captured PID: [$STRIPE_LISTEN_PID]"
if [ -z "$STRIPE_LISTEN_PID" ]; then
echo "Critical Error: Failed to capture PID of persistent Stripe listen process immediately after launch (PID was empty)."
exit 1 # Should not happen if stripe listen & itself doesn't error out before $! is assigned
fi
echo "Waiting a moment for persistent Stripe listen (PID: $STRIPE_LISTEN_PID) to initialize..."
sleep 3 # Increased sleep slightly
if ! ps -p "$STRIPE_LISTEN_PID" > /dev/null; then
echo "Error: Persistent background Stripe listen process (PID: $STRIPE_LISTEN_PID) failed to start or died shortly after starting."
echo "Check for errors in the terminal output above from Stripe CLI (it logs directly when run with '&')."
# STRIPE_LISTEN_PID is kept so cleanup knows which PID it was attempting to manage
exit 1 # This will trigger cleanup
fi
echo "Persistent background Stripe listen started (PID: $STRIPE_LISTEN_PID). Forwarding events."
echo "API logs will follow. Stripe listen logs will be mixed with API logs."
echo "---"
# Run the API command with the webhook secret
echo "Executing API command with injected -e STRIPE_WEBHOOK_SECRET=whsec_... (masked)"
# API_RUN_COMMAND is ("docker" "run" "-p" "8000:8000" ...)
# We want ("docker" "run" "-e" "STRIPE_WEBHOOK_SECRET=value" "-p" "8000:8000" ...)
CMD_ARRAY=()
CMD_ARRAY+=("${API_RUN_COMMAND[0]}") # docker
CMD_ARRAY+=("${API_RUN_COMMAND[1]}") # run
CMD_ARRAY+=("-e")
CMD_ARRAY+=("STRIPE_WEBHOOK_SECRET=${WEBHOOK_SECRET}")
# Add the rest of the original command, skipping the first two elements ("docker" "run")
# This assumes that API_RUN_COMMAND always starts with "docker run"
# Ensure elements from index 2 onwards are correctly appended.
if [ ${#API_RUN_COMMAND[@]} -gt 2 ]; then
CMD_ARRAY+=("${API_RUN_COMMAND[@]:2}")
fi
"${CMD_ARRAY[@]}"
API_EXIT_CODE=$?
echo "API command exited with code $API_EXIT_CODE."
exit $API_EXIT_CODE