diff --git a/.env.develop.example b/.env.develop.example new file mode 100644 index 0000000..88b274b --- /dev/null +++ b/.env.develop.example @@ -0,0 +1,87 @@ +APP_NAME=SonarDesk +APP_ENV=local +APP_KEY= +APP_DEBUG=true +APP_URL=https://sonardesk.vm + +APP_LOCALE=en +APP_FALLBACK_LOCALE=en +APP_FAKER_LOCALE=en_US + +APP_MAINTENANCE_DRIVER=file +# APP_MAINTENANCE_STORE=database + +# PHP_CLI_SERVER_WORKERS=4 + +BCRYPT_ROUNDS=12 + +LOG_CHANNEL=stack +LOG_STACK=single +LOG_DEPRECATIONS_CHANNEL=null +LOG_LEVEL=debug + +DB_CONNECTION=sqlite +# DB_HOST=127.0.0.1 +# DB_PORT=3306 +# DB_DATABASE=laravel +# DB_USERNAME=root +# DB_PASSWORD= + +SESSION_DRIVER=database +SESSION_LIFETIME=120 +SESSION_ENCRYPT=false +SESSION_PATH=/ +SESSION_DOMAIN=null + +BROADCAST_CONNECTION=log +FILESYSTEM_DISK=local +QUEUE_CONNECTION=database + +CACHE_STORE=database +# CACHE_PREFIX= + +MEMCACHED_HOST=127.0.0.1 + +REDIS_CLIENT=phpredis +REDIS_HOST=127.0.0.1 +REDIS_PASSWORD=null +REDIS_PORT=6379 + +MAIL_MAILER=smtp +MAIL_SCHEME=null +MAIL_HOST=127.0.0.1 +MAIL_PORT=1025 +MAIL_USERNAME=null +MAIL_PASSWORD=null +MAIL_FROM_ADDRESS="licensing@sonardesk.absdev.net" +MAIL_FROM_NAME="SonarDesk" + +AWS_ACCESS_KEY_ID= +AWS_SECRET_ACCESS_KEY= +AWS_DEFAULT_REGION=us-east-1 +AWS_BUCKET= +AWS_USE_PATH_STYLE_ENDPOINT=false + +VITE_APP_NAME="${APP_NAME}" + +# SonarDesk commercial/licensing +SONARDESK_ENVIRONMENT=develop +SONARDESK_LICENSE_PRIVATE_KEY_PATH= +SONARDESK_LICENSE_PUBLIC_KEY= +SONARDESK_DEFAULT_EDITION=Professional +SONARDESK_DEFAULT_MAX_MAJOR_VERSION=1 +SONARDESK_PURCHASE_URL=https://sonardesk.vm/buy +SONARDESK_PORTAL_URL=https://sonardesk.vm/portal +SONARDESK_SUPPORT_URL=https://sonardesk.vm/support +PADDLE_API_KEY= +PADDLE_API_URL=https://sandbox-api.paddle.com +PADDLE_CLIENT_SIDE_TOKEN= +PADDLE_ENVIRONMENT=sandbox +PADDLE_WEBHOOK_SECRET= +PADDLE_WEBHOOK_TOLERANCE=300 +PADDLE_PRODUCT_ID= +PADDLE_PERSONAL_MONTHLY_PRICE_ID= +PADDLE_PERSONAL_ANNUAL_PRICE_ID= +PADDLE_PROFESSIONAL_MONTHLY_PRICE_ID= +PADDLE_PROFESSIONAL_ANNUAL_PRICE_ID= +LEMON_SQUEEZY_WEBHOOK_SECRET= diff --git a/.env.production.example b/.env.production.example new file mode 100644 index 0000000..50698af --- /dev/null +++ b/.env.production.example @@ -0,0 +1,87 @@ +APP_NAME=SonarDesk +APP_ENV=production +APP_KEY= +APP_DEBUG=false +APP_URL=https://sonardsk.absdev.net + +APP_LOCALE=en +APP_FALLBACK_LOCALE=en +APP_FAKER_LOCALE=en_US + +APP_MAINTENANCE_DRIVER=file +# APP_MAINTENANCE_STORE=database + +# PHP_CLI_SERVER_WORKERS=4 + +BCRYPT_ROUNDS=12 + +LOG_CHANNEL=stack +LOG_STACK=single +LOG_DEPRECATIONS_CHANNEL=null +LOG_LEVEL=debug + +DB_CONNECTION=sqlite +# DB_HOST=127.0.0.1 +# DB_PORT=3306 +# DB_DATABASE=laravel +# DB_USERNAME=root +# DB_PASSWORD= + +SESSION_DRIVER=database +SESSION_LIFETIME=120 +SESSION_ENCRYPT=false +SESSION_PATH=/ +SESSION_DOMAIN=null + +BROADCAST_CONNECTION=log +FILESYSTEM_DISK=local +QUEUE_CONNECTION=database + +CACHE_STORE=database +# CACHE_PREFIX= + +MEMCACHED_HOST=127.0.0.1 + +REDIS_CLIENT=phpredis +REDIS_HOST=127.0.0.1 +REDIS_PASSWORD=null +REDIS_PORT=6379 + +MAIL_MAILER=smtp +MAIL_SCHEME=null +MAIL_HOST=CHANGE_ME +MAIL_PORT=587 +MAIL_USERNAME=null +MAIL_PASSWORD=null +MAIL_FROM_ADDRESS="licensing@sonardesk.absdev.net" +MAIL_FROM_NAME="SonarDesk" + +AWS_ACCESS_KEY_ID= +AWS_SECRET_ACCESS_KEY= +AWS_DEFAULT_REGION=us-east-1 +AWS_BUCKET= +AWS_USE_PATH_STYLE_ENDPOINT=false + +VITE_APP_NAME="${APP_NAME}" + +# SonarDesk commercial/licensing +SONARDESK_ENVIRONMENT=production +SONARDESK_LICENSE_PRIVATE_KEY_PATH= +SONARDESK_LICENSE_PUBLIC_KEY= +SONARDESK_DEFAULT_EDITION=Professional +SONARDESK_DEFAULT_MAX_MAJOR_VERSION=1 +SONARDESK_PURCHASE_URL=https://sonardsk.absdev.net/buy +SONARDESK_PORTAL_URL=https://sonardsk.absdev.net/portal +SONARDESK_SUPPORT_URL=https://sonardsk.absdev.net/support +PADDLE_API_KEY= +PADDLE_API_URL=https://api.paddle.com +PADDLE_CLIENT_SIDE_TOKEN= +PADDLE_ENVIRONMENT=production +PADDLE_WEBHOOK_SECRET= +PADDLE_WEBHOOK_TOLERANCE=300 +PADDLE_PRODUCT_ID= +PADDLE_PERSONAL_MONTHLY_PRICE_ID= +PADDLE_PERSONAL_ANNUAL_PRICE_ID= +PADDLE_PROFESSIONAL_MONTHLY_PRICE_ID= +PADDLE_PROFESSIONAL_ANNUAL_PRICE_ID= +LEMON_SQUEEZY_WEBHOOK_SECRET= diff --git a/.env.stage.example b/.env.stage.example new file mode 100644 index 0000000..527e4fd --- /dev/null +++ b/.env.stage.example @@ -0,0 +1,87 @@ +APP_NAME=SonarDesk +APP_ENV=staging +APP_KEY= +APP_DEBUG=false +APP_URL=https://sonardesk.stage.absdev.net + +APP_LOCALE=en +APP_FALLBACK_LOCALE=en +APP_FAKER_LOCALE=en_US + +APP_MAINTENANCE_DRIVER=file +# APP_MAINTENANCE_STORE=database + +# PHP_CLI_SERVER_WORKERS=4 + +BCRYPT_ROUNDS=12 + +LOG_CHANNEL=stack +LOG_STACK=single +LOG_DEPRECATIONS_CHANNEL=null +LOG_LEVEL=debug + +DB_CONNECTION=sqlite +# DB_HOST=127.0.0.1 +# DB_PORT=3306 +# DB_DATABASE=laravel +# DB_USERNAME=root +# DB_PASSWORD= + +SESSION_DRIVER=database +SESSION_LIFETIME=120 +SESSION_ENCRYPT=false +SESSION_PATH=/ +SESSION_DOMAIN=null + +BROADCAST_CONNECTION=log +FILESYSTEM_DISK=local +QUEUE_CONNECTION=database + +CACHE_STORE=database +# CACHE_PREFIX= + +MEMCACHED_HOST=127.0.0.1 + +REDIS_CLIENT=phpredis +REDIS_HOST=127.0.0.1 +REDIS_PASSWORD=null +REDIS_PORT=6379 + +MAIL_MAILER=smtp +MAIL_SCHEME=null +MAIL_HOST=CHANGE_ME +MAIL_PORT=587 +MAIL_USERNAME=null +MAIL_PASSWORD=null +MAIL_FROM_ADDRESS="licensing@sonardesk.absdev.net" +MAIL_FROM_NAME="SonarDesk" + +AWS_ACCESS_KEY_ID= +AWS_SECRET_ACCESS_KEY= +AWS_DEFAULT_REGION=us-east-1 +AWS_BUCKET= +AWS_USE_PATH_STYLE_ENDPOINT=false + +VITE_APP_NAME="${APP_NAME}" + +# SonarDesk commercial/licensing +SONARDESK_ENVIRONMENT=stage +SONARDESK_LICENSE_PRIVATE_KEY_PATH= +SONARDESK_LICENSE_PUBLIC_KEY= +SONARDESK_DEFAULT_EDITION=Professional +SONARDESK_DEFAULT_MAX_MAJOR_VERSION=1 +SONARDESK_PURCHASE_URL=https://sonardesk.stage.absdev.net/buy +SONARDESK_PORTAL_URL=https://sonardesk.stage.absdev.net/portal +SONARDESK_SUPPORT_URL=https://sonardesk.stage.absdev.net/support +PADDLE_API_KEY= +PADDLE_API_URL=https://sandbox-api.paddle.com +PADDLE_CLIENT_SIDE_TOKEN= +PADDLE_ENVIRONMENT=sandbox +PADDLE_WEBHOOK_SECRET= +PADDLE_WEBHOOK_TOLERANCE=300 +PADDLE_PRODUCT_ID= +PADDLE_PERSONAL_MONTHLY_PRICE_ID= +PADDLE_PERSONAL_ANNUAL_PRICE_ID= +PADDLE_PROFESSIONAL_MONTHLY_PRICE_ID= +PADDLE_PROFESSIONAL_ANNUAL_PRICE_ID= +LEMON_SQUEEZY_WEBHOOK_SECRET= diff --git a/app/Console/Commands/SonarDeskLicenseDeliver.php b/app/Console/Commands/SonarDeskLicenseDeliver.php new file mode 100644 index 0000000..68d5efe --- /dev/null +++ b/app/Console/Commands/SonarDeskLicenseDeliver.php @@ -0,0 +1,20 @@ +option('limit'), 100)); + $count = $delivery->deliverPending($limit); + $this->info("Delivered {$count} licence email(s)."); + return self::SUCCESS; + } +} diff --git a/docs/RELEASE-HARDENING.md b/docs/RELEASE-HARDENING.md new file mode 100644 index 0000000..afefa8a --- /dev/null +++ b/docs/RELEASE-HARDENING.md @@ -0,0 +1,11 @@ + +## Licensing/email release checks + +- Production app endpoint: `https://sonardsk.absdev.net`. +- Stage app endpoint: `https://sonardesk.stage.absdev.net`. +- Production must use Paddle production credentials and the production webhook destination. +- `MAIL_MAILER` must be a real delivery transport in production; never `log`. +- Run the Laravel scheduler continuously so pending licence-key emails retry automatically. +- Verify `php artisan sonardesk:license-deliver` reports no unexpected pending delivery failures. +- Verify a real Paddle purchase creates one licence, sends one key email, activates a clean Mac, validates after relaunch, and deactivates from the portal. +- Never deploy `.env`, signing seeds, Paddle secrets, or webhook secrets from source control. diff --git a/resources/views/emails/license-key-text.blade.php b/resources/views/emails/license-key-text.blade.php new file mode 100644 index 0000000..cdaf808 --- /dev/null +++ b/resources/views/emails/license-key-text.blade.php @@ -0,0 +1,20 @@ +SonarDesk — Your licence is ready + +{{ $customerName ? 'Hello '.$customerName.',' : 'Hello,' }} + +Thank you for choosing SonarDesk. Your {{ $edition }} licence key is: + +{{ $licenseKey }} + +To activate: +1. Open SonarDesk. +2. Open Settings → Licence. +3. Paste the key above and select Activate. + +@if($expiresAt)Current entitlement period: {{ $expiresAt->format('j M Y') }}. + +@endif +@if($portalUrl)Manage your licence: {{ $portalUrl }} + +@endif +Keep this key private. diff --git a/resources/views/emails/license-key.blade.php b/resources/views/emails/license-key.blade.php new file mode 100644 index 0000000..bc23ef9 --- /dev/null +++ b/resources/views/emails/license-key.blade.php @@ -0,0 +1,19 @@ + + +
| + + |