Releases: SmartAppsCo/Paycheck
Releases · SmartAppsCo/Paycheck
v0.8.1
Added
- Test coverage: Email validation, SSRF prevention, auth/impersonation, feedback/crash endpoints, SDK token verification, service config CRUD, cross-org isolation, webhook error handling
- Bruno API collection files for operator tag endpoints and metering service
Deserializederive on metering event types for downstream consumption
Changed
- Replaced
jwt-simplewithjsonwebtoken: Migrated from BoringSSL backend to ring backend, eliminating heavy build dependencies (cmake, golang, libclang) from Docker builds - Removed dead JWKS/first-party JWT code: Deleted speculative
JwksCache,TrustedIssuer, and RSA handling that was never used (console uses API keys via proxy pattern) - Graceful Docker shutdown: Server now handles SIGTERM in addition to SIGINT, preventing force-kill (exit code 137) on
docker stop - Removed
internal/from.dockerignore
Fixed
- SSRF prevention:
validate_webhook_url()now blocks IPv4-mapped IPv6 addresses,0.0.0.0,::, and 172.16-31.x DNS rebinding hostnames - Email validation:
validate_email_format()rejects CRLF injection, null bytes, and enforces RFC 5321 length limits - Activation code request: Now validates email format before processing
Full Changelog: v0.8.0...v0.8.1
v0.8.0
Added
- Tagging system for users and organizations: Flexible tagging with add/remove semantics where remove takes precedence over add
- Tags stored as JSON arrays in the database
- New
PATCH /operators/users/{id}/tagsendpoint for user tags (admin+ required) - New
PATCH /operators/organizations/{id}/tagsendpoint for org tags (admin+ required) - Request body:
{"add": ["tag1", "tag2"], "remove": ["tag3"]}— remove takes precedence
- Org tag enforcement for public API endpoints: Configurable tag-based blocking for organizations
PAYCHECK_DISABLE_CHECKOUT_TAG: BlocksPOST /buywhen org has matching tagPAYCHECK_DISABLE_PUBLIC_API_TAG: Blocks/buy,/validate,/activation/request-code, and/refresh- Returns 503 Service Unavailable when org has the matching tag
- No blocking when env vars are not set (self-hosted friendly)
Fixed
- Unnecessary backup on fresh databases: Migration system now skips backup for version 0 databases (nothing to backup)
Full Changelog: v0.7.2...v0.8.0
v0.7.2
Fixed
- JTI prefix consistency: New JWT IDs now use
pc_jti_prefix viaEntityType::Jti.gen_id()(existing tokens unaffected - stored JTI is reused on refresh)
Full Changelog: v0.7.1...v0.7.2
v0.7.1
Fixed
- API key prefix consistency: New API keys now use
pc_key_prefix viaEntityType::ApiKey.gen_id()(existing keys unaffected - lookup is by hash) - Docs site logo now navigates in same window instead of opening new tab
Full Changelog: v0.7.0...v0.7.1
v0.7.0
Added
- Prefixed entity IDs: All IDs now use
pc_{entity}_{32_hex_chars}format (e.g.,pc_usr_,pc_org_,pc_lic_)- Instant entity type identification in logs and support tickets
- Collision avoidance with payment provider IDs (Stripe's
prod_,cus_,sub_) - New
src/id.rsmodule withEntityTypeenum for 15 entity types
- Transaction tracking: Revenue analytics decoupled from licenses
GET /orgs/{org_id}/transactions[/stats]— org-level revenueGET /orgs/{org_id}/projects/{project_id}/transactions[/stats]— project-level revenueGET /orgs/{org_id}/projects/{project_id}/licenses/{license_id}/transactions— license history- Filters:
product_id,transaction_type,date_range,test_mode - Stats:
total_revenue,net_revenue,transactions_by_type
- Feedback and crash reporting: Passthrough to dev-configured endpoints (no PII storage)
POST /feedbackandPOST /crashendpoints (JWT auth required)- Webhook (primary) + email (fallback) delivery with exponential backoff
- Project config:
feedback_webhook_url,feedback_email,crash_webhook_url,crash_email - SDK methods:
submitFeedback(),reportCrash(),reportError()with stack trace sanitization
- Usage metering webhook: Optional billing events for hosted deployments
- Email events:
activation_sent,feedback_sent,crash_sentwithdelivery_method - Sales events:
purchase,renewal,refundwith transaction details - Configure via
PAYCHECK_METERING_WEBHOOK_URL
- Email events:
- Refund handling: Stripe and LemonSqueezy refund webhooks create
refundtransactions - Subscription renewal transactions: Renewal webhooks now create transaction records
- 3-level email config lookup: Product → Project → Org inheritance for
email_config_id - Configurable request body size:
PAYCHECK_MAX_BODY_SIZEenv var (default: 1MB) - Docusaurus documentation site: Comprehensive docs at docs.paycheck.dev
- SDK issuer validation: Rust and TypeScript SDKs reject JWTs not issued by Paycheck
Changed
- Breaking:
/validateendpoint now accepts full JWT token instead of justjti - README simplified to point to hosted documentation
- SDK package metadata updated with correct license (Elastic-2.0) and repository URL
- Use
OsRnginstead ofthread_rng()for all cryptographic operations - Memory limits added to activation rate limiter (DoS prevention)
Fixed
- Project restore endpoint unreachable: Moved to
org_routeslayer (middleware was rejecting soft-deleted projects) - Service config deletion: Now uses TOCTOU-safe transaction
- Soft delete restore cascades:
project_membersnow properly restored with parent - Queries excluding soft-deleted entities: Joined entity lookups now filter
deleted_at - CORS headers: Added
x-on-behalf-ofto allowed headers - Feedback billing fairness: Metering correctly reports
org_keyvssystem_keyfor all email types
Full Changelog: v0.6.2...v0.7.0
v0.6.2
Fixed
- Payment callback webhook race condition: Callback endpoint now polls for up to 500ms before returning
status=pending, catching cases where the browser redirect beats the webhook delivery (typically 100-300ms)
Full Changelog: v0.6.1...v0.6.2
v0.6.1
Changed
- SDKs normalize activation codes: Non-alphanumeric characters (backticks, dots, underscores, etc.) are stripped before validation
- Handles messy copy-paste from emails gracefully (e.g.,
`C9MA-JUFF`→C9MA-JUFF) - Multiple separators collapsed into single dashes
- Handles messy copy-paste from emails gracefully (e.g.,
- Project prefix validation:
license_key_prefixmust contain only alphanumeric characters- Ensures SDK normalization works correctly (non-alphanumeric chars are treated as separators)
Fixed
- Email template uses
<span>instead of<code>to prevent some email clients from adding backticks when copying activation codes
Full Changelog: v0.6.0...v0.6.1
v0.6.0
Added
- Expanded license update API:
PUT /orgs/.../licenses/{id}now supports updatingcustomer_id,expires_at, andupdates_expires_at- Nullable fields can be explicitly set to null via
Option<Option<T>>pattern
- Nullable fields can be explicitly set to null via
- Optional activation code prefix: Server accepts both
PREFIX-XXXX-XXXXand bareXXXX-XXXXformats- Bare codes get project prefix prepended before hashing (no collision risk)
- SDKs validate both formats before making network requests
- Allows apps to display prefix as a visual hint without requiring user input
Changed
- Breaking (Rust SDK): Refactored to sync-first for desktop app compatibility
- Replaced async
reqwest+tokiowith syncureq(no async runtime required) - Removed
MemoryStorage— desktop apps should use persistent file storage - Constructor now takes explicit
storage_dirpath instead of app name - Auto-creates storage directory if it doesn't exist
- Split API:
new()for defaults,with_options()for custom configuration - Simplified TLS feature flags:
rustls-tls(default) ornative-tls
- Replaced async
- Breaking: License update endpoint changed from
PATCHtoPUTfor consistency - Audit action renamed:
UpdateLicenseEmail→UpdateLicense
Full Changelog: v0.5.0...v0.6.0
v0.5.0
Added
- Named service configs: Reusable config pool at the org level with user-friendly names
- New CRUD endpoints:
GET/POST /orgs/{org_id}/service-configs,GET/PUT/DELETE /orgs/{org_id}/service-configs/{id} - Configs have
name,category(payment/email), andprovider(stripe/lemonsqueezy/resend) - Configs can be shared across projects and products via FK references
- New CRUD endpoints:
- Three-level config inheritance: Product → Project → Org for both payment and email configs
payment_config_idandemail_config_idfields on organizations, projects, and products- Lookup functions check product first, then project, then org
ConfigSourceenum indicates where the effective config came from
- Cancel URL support: Payment checkout cancel redirects to project's
redirect_urlwith?status=canceledquery parameter - Database migration 002 for existing databases (adds new columns, migrates existing configs)
Changed
- Breaking: Service configs now use named configs instead of scope-based configs
- Old
ServiceScopeenum removed; configs are now org-owned with FK references stripe_config/ls_config/resend_api_keyfields in API replaced withpayment_config_id/email_config_id
- Old
- Breaking:
get_effective_email_config()now requires aproductparameter for 3-level lookup - Breaking: Organization model no longer stores payment credentials directly
- Use
payment_config_idandemail_config_idto reference service configs
- Use
- Provider link documentation clarified to distinguish from service configs (provider links map products to payment provider price IDs)
Full Changelog: v0.4.0...v0.5.0
v0.4.0
Added
- Database migration system with automatic backup before schema changes
- Migrations run on startup, tracked via
PRAGMA user_version - Creates timestamped backups (e.g.,
paycheck.db.backup_v0_20260120_143022) - Configure retention with
MIGRATION_BACKUP_COUNT(default: 3, -1 = keep all, 0 = none)
- Migrations run on startup, tracked via
Changed
- Subscription renewals now use the payment provider's billing period end date instead of calculating from
license_exp_days
Full Changelog: v0.3.0...v0.4.0