#!/usr/bin/env bash set -euo pipefail PADDLE_API_URL="https://api.paddle.com" : "${PADDLE_API_KEY:?PADDLE_API_KEY is not set}" : "${PADDLE_PRODUCT_ID:?PADDLE_PRODUCT_ID is not set}" command -v curl >/dev/null || { echo "ERROR: curl is required." exit 1 } command -v jq >/dev/null || { echo "ERROR: jq is required." exit 1 } if [[ "$PADDLE_API_KEY" != pdl_live_apikey_* ]]; then echo "ERROR: This script requires a Paddle LIVE API key." exit 1 fi if [[ "$PADDLE_PRODUCT_ID" != pro_* ]]; then echo "ERROR: Invalid Paddle product ID." exit 1 fi echo "============================================" echo " SonarDesk Paddle Production Price Setup" echo "============================================" echo echo "Product: $PADDLE_PRODUCT_ID" echo # ------------------------------------------------------------ # Verify product # ------------------------------------------------------------ PRODUCT_RESPONSE="$( curl -fsS \ -H "Authorization: Bearer $PADDLE_API_KEY" \ -H "Accept: application/json" \ "$PADDLE_API_URL/products/$PADDLE_PRODUCT_ID" )" PRODUCT_NAME="$(echo "$PRODUCT_RESPONSE" | jq -r '.data.name // empty')" if [[ -z "$PRODUCT_NAME" ]]; then echo "ERROR: Could not retrieve Paddle product." exit 1 fi echo "Found product: $PRODUCT_NAME" echo # ------------------------------------------------------------ # Existing prices # ------------------------------------------------------------ get_prices() { curl -fsS \ -H "Authorization: Bearer $PADDLE_API_KEY" \ -H "Accept: application/json" \ "$PADDLE_API_URL/prices?product_id=$PADDLE_PRODUCT_ID&status=active&per_page=100" } find_price_by_description() { local description="$1" get_prices | jq -r \ --arg description "$description" \ '.data[] | select(.description == $description) | .id' | head -n 1 } create_price() { local description="$1" local amount="$2" local interval="$3" echo "Creating: $description" curl -fsS \ -X POST \ "$PADDLE_API_URL/prices" \ -H "Authorization: Bearer $PADDLE_API_KEY" \ -H "Content-Type: application/json" \ -H "Accept: application/json" \ --data "$( jq -n \ --arg product "$PADDLE_PRODUCT_ID" \ --arg description "$description" \ --arg amount "$amount" \ --arg interval "$interval" \ '{ product_id: $product, description: $description, unit_price: { amount: $amount, currency_code: "GBP" }, billing_cycle: { interval: $interval, frequency: 1 }, quantity: { minimum: 1, maximum: 1 }, tax_mode: "account_setting" }' )" | jq -r '.data.id' } update_price_amount() { local price_id="$1" local description="$2" local amount="$3" echo "Updating: $description ($price_id)" curl -fsS \ -X PATCH \ "$PADDLE_API_URL/prices/$price_id" \ -H "Authorization: Bearer $PADDLE_API_KEY" \ -H "Content-Type: application/json" \ -H "Accept: application/json" \ --data "$( jq -n \ --arg description "$description" \ --arg amount "$amount" \ '{ description: $description, unit_price: { amount: $amount, currency_code: "GBP" } }' )" >/dev/null } ensure_price() { local description="$1" local amount="$2" local interval="$3" local existing existing="$(find_price_by_description "$description")" if [[ -n "$existing" ]]; then local current_amount current_amount="$( get_prices | jq -r \ --arg id "$existing" \ '.data[] | select(.id == $id) | .unit_price.amount' )" if [[ "$current_amount" != "$amount" ]]; then echo "Existing price $existing has amount $current_amount." echo "Required amount is $amount." update_price_amount \ "$existing" \ "$description" \ "$amount" else echo "Exists: $description ($existing)" fi echo "$existing" return fi create_price \ "$description" \ "$amount" \ "$interval" } # ------------------------------------------------------------ # SonarDesk production prices # # Paddle amounts are in pennies: # # £2.99 = 299 # £24.99 = 2499 # £4.99 = 499 # £44.99 = 4499 # ------------------------------------------------------------ echo "Configuring prices..." echo PERSONAL_MONTHLY="$( ensure_price \ "SonarDesk Personal Monthly" \ "299" \ "month" | tail -n 1 )" PERSONAL_ANNUAL="$( ensure_price \ "SonarDesk Personal Annual" \ "2499" \ "year" | tail -n 1 )" PROFESSIONAL_MONTHLY="$( ensure_price \ "SonarDesk Professional Monthly" \ "499" \ "month" | tail -n 1 )" PROFESSIONAL_ANNUAL="$( ensure_price \ "SonarDesk Professional Annual" \ "4499" \ "year" | tail -n 1 )" echo echo "============================================" echo " Paddle Production Prices" echo "============================================" echo echo "Personal Monthly: $PERSONAL_MONTHLY" echo "Personal Annual: $PERSONAL_ANNUAL" echo "Professional Monthly: $PROFESSIONAL_MONTHLY" echo "Professional Annual: $PROFESSIONAL_ANNUAL" echo echo "Add these to the production .env:" echo echo "PADDLE_ENVIRONMENT=production" echo "PADDLE_API_URL=https://api.paddle.com" echo "PADDLE_PRODUCT_ID=$PADDLE_PRODUCT_ID" echo "PADDLE_PERSONAL_MONTHLY_PRICE_ID=$PERSONAL_MONTHLY" echo "PADDLE_PERSONAL_ANNUAL_PRICE_ID=$PERSONAL_ANNUAL" echo "PADDLE_PROFESSIONAL_MONTHLY_PRICE_ID=$PROFESSIONAL_MONTHLY" echo "PADDLE_PROFESSIONAL_ANNUAL_PRICE_ID=$PROFESSIONAL_ANNUAL" echo echo "IMPORTANT:" echo "PADDLE_API_KEY and PADDLE_CLIENT_SIDE_TOKEN must also be LIVE values." echo "PADDLE_WEBHOOK_SECRET must come from the LIVE webhook destination." echo