diff --git a/.env.docker.production.example b/.env.docker.production.example index 0effdc8..db7cb3d 100644 --- a/.env.docker.production.example +++ b/.env.docker.production.example @@ -87,8 +87,9 @@ MAIL_REPLY_TO_ADDRESS=rentaldrivego@gmail.com MAIL_REPLY_TO_NAME=RentalDriveGo # Stripe subscription checkout -# STRIPE_API_KEY must be a Stripe secret/restricted key (sk_ or rk_). +# STRIPE_API_KEY must be a live Stripe secret/restricted key (sk_live_ or rk_live_). # STRIPE_WEBHOOK_SECRET must be a Stripe webhook signing secret (whsec_). +# Gitea deploys can override these from STRIPE_API_KEY and STRIPE_WEBHOOK_SECRET secrets. STRIPE_API_KEY=placeholder STRIPE_WEBHOOK_SECRET=placeholder diff --git a/.gitea/workflows/build-and-deploy.yml b/.gitea/workflows/build-and-deploy.yml index 2343ee0..16c0b27 100644 --- a/.gitea/workflows/build-and-deploy.yml +++ b/.gitea/workflows/build-and-deploy.yml @@ -428,6 +428,8 @@ jobs: IMAGE_TAG: ${{ gitea.sha }} ENV_DOCKER_PRODUCTION: ${{ secrets.ENV_DOCKER_PRODUCTION }} ENV_DOCKER_PRODUCTION_B64: ${{ secrets.ENV_DOCKER_PRODUCTION_B64 }} + STRIPE_API_KEY: ${{ secrets.STRIPE_API_KEY }} + STRIPE_WEBHOOK_SECRET: ${{ secrets.STRIPE_WEBHOOK_SECRET }} REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }} REGISTRY_USER: ${{ secrets.REGISTRY_USER }} REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }} @@ -443,6 +445,8 @@ jobs: SSH_OPTIONS="-i $HOME/.ssh/id_rsa -o BatchMode=yes -o IdentitiesOnly=yes -o StrictHostKeyChecking=accept-new -o UserKnownHostsFile=$HOME/.ssh/known_hosts" ENV_DOCKER_PRODUCTION_B64_CLEAN="$(printf '%s' "$ENV_DOCKER_PRODUCTION_B64" | tr -d '[:space:]')" ENV_DOCKER_PRODUCTION_RAW_B64="$(printf '%s' "$ENV_DOCKER_PRODUCTION" | base64 | tr -d '\n')" + STRIPE_API_KEY_B64="$(printf '%s' "$STRIPE_API_KEY" | base64 | tr -d '\n')" + STRIPE_WEBHOOK_SECRET_B64="$(printf '%s' "$STRIPE_WEBHOOK_SECRET" | base64 | tr -d '\n')" REGISTRY_USERNAME="${REGISTRY_USERNAME:-${REGISTRY_USER:-}}" REGISTRY_PASSWORD="${REGISTRY_PASSWORD:-${REGISTRY_TOKEN:-}}" REGISTRY_PASSWORD_B64="$(printf '%s' "$REGISTRY_PASSWORD" | base64 | tr -d '\n')" @@ -464,6 +468,9 @@ jobs: cp '$DEPLOY_ROOT/.env.docker.production' '$REMOTE_BUILD_DIR/.env.docker.production' chmod 600 '$REMOTE_BUILD_DIR/.env.docker.production' cd '$REMOTE_BUILD_DIR' + export STRIPE_API_KEY_B64='$STRIPE_API_KEY_B64' + export STRIPE_WEBHOOK_SECRET_B64='$STRIPE_WEBHOOK_SECRET_B64' + bash scripts/apply-env-secret-overrides.sh .env.docker.production STRIPE_API_KEY STRIPE_WEBHOOK_SECRET export APP_IMAGE='$DEPLOY_REGISTRY_HOST/$IMAGE_REPOSITORY' export IMAGE_TAG='$IMAGE_TAG' export REGISTRY_HOST='$DEPLOY_REGISTRY_HOST' diff --git a/production/.env.docker.production.example b/production/.env.docker.production.example index 5c6e46b..9adef10 100644 --- a/production/.env.docker.production.example +++ b/production/.env.docker.production.example @@ -86,6 +86,7 @@ MAIL_REPLY_TO_ADDRESS=rentaldrivego@gmail.com MAIL_REPLY_TO_NAME=RentalDriveGo # Stripe subscription checkout -# Prefer a restricted API key (rk_) with the minimum Billing/Checkout permissions. +# Prefer a live restricted API key (rk_live_) with the minimum Billing/Checkout permissions. +# Gitea deploys can override these from STRIPE_API_KEY and STRIPE_WEBHOOK_SECRET secrets. STRIPE_API_KEY=placeholder STRIPE_WEBHOOK_SECRET=placeholder diff --git a/production/scripts/docker-prod-common.sh b/production/scripts/docker-prod-common.sh index 4d23596..d74d500 100755 --- a/production/scripts/docker-prod-common.sh +++ b/production/scripts/docker-prod-common.sh @@ -233,8 +233,8 @@ validate_prod_env_file() { exit 1 fi - if [[ "${stripe_api_key}" != sk_* && "${stripe_api_key}" != rk_* ]]; then - echo "Invalid STRIPE_API_KEY in ${ENV_FILE}. It must start with sk_ or rk_." >&2 + if [[ "${stripe_api_key}" != sk_live_* && "${stripe_api_key}" != rk_live_* ]]; then + echo "Invalid STRIPE_API_KEY in ${ENV_FILE}. It must start with sk_live_ or rk_live_ for production billing." >&2 exit 1 fi diff --git a/scripts/apply-env-secret-overrides.sh b/scripts/apply-env-secret-overrides.sh new file mode 100644 index 0000000..3d74323 --- /dev/null +++ b/scripts/apply-env-secret-overrides.sh @@ -0,0 +1,78 @@ +#!/usr/bin/env bash + +set -euo pipefail + +usage() { + cat <<'EOF' +Usage: + apply-env-secret-overrides.sh [KEY...] + +For each KEY, this script reads KEY_B64 from the environment, base64-decodes it, +and upserts KEY= in . Empty KEY_B64 values are ignored. +EOF +} + +if [[ $# -lt 2 ]]; then + usage >&2 + exit 1 +fi + +env_file="$1" +shift + +if [[ ! -f "${env_file}" ]]; then + echo "Missing env file: ${env_file}" >&2 + exit 1 +fi + +upsert_env_value() { + local key="$1" + local value="$2" + local tmp_file + + tmp_file="$(mktemp "${env_file}.tmp.XXXXXX")" + awk -v key="${key}" -v value="${value}" ' + BEGIN { + updated = 0 + } + /^[[:space:]]*#/ || index($0, "=") == 0 { + print + next + } + { + name = $1 + sub(/=.*/, "", name) + sub(/^[[:space:]]*export[[:space:]]+/, "", name) + gsub(/^[[:space:]]+|[[:space:]]+$/, "", name) + if (name == key) { + print key "=" value + updated = 1 + next + } + print + } + END { + if (!updated) { + print key "=" value + } + } + ' "${env_file}" > "${tmp_file}" + + mv "${tmp_file}" "${env_file}" +} + +for key in "$@"; do + encoded_var="${key}_B64" + encoded_value="${!encoded_var:-}" + + if [[ -z "${encoded_value}" ]]; then + continue + fi + + if ! decoded_value="$(printf '%s' "${encoded_value}" | base64 -d)"; then + echo "Invalid base64 value in ${encoded_var}" >&2 + exit 1 + fi + + upsert_env_value "${key}" "${decoded_value}" +done diff --git a/scripts/docker-prod-common.sh b/scripts/docker-prod-common.sh index 93f8093..f444f96 100644 --- a/scripts/docker-prod-common.sh +++ b/scripts/docker-prod-common.sh @@ -228,8 +228,8 @@ validate_prod_env_file() { exit 1 fi - if [[ "${stripe_api_key}" != sk_* && "${stripe_api_key}" != rk_* ]]; then - echo "Invalid STRIPE_API_KEY in ${ENV_FILE}. It must start with sk_ or rk_." >&2 + if [[ "${stripe_api_key}" != sk_live_* && "${stripe_api_key}" != rk_live_* ]]; then + echo "Invalid STRIPE_API_KEY in ${ENV_FILE}. It must start with sk_live_ or rk_live_ for production billing." >&2 exit 1 fi