Update Stripe publishable key storage in Stripe Settings

1. Updated UpdateStripeSettingsRequest - Added publishable_key field
2. Updated update_stripe_settings endpoint - Now validates and stores:
	- stripe_publishable_key (not encrypted - it's public)
	- stripe_secret_key (encrypted)
	- stripe_webhook_secret (encrypted)
	- Also validates that publishable and secret keys are from the same environment (both test or both live)
3. Added new public endpoint GET /api/config/stripe - Returns the publishable key to the frontend (no auth required since it's meant to be public)
4. Updated get_stripe_status endpoint - Now includes publishable_key_prefix and publishable_key_set in the response
This commit is contained in:
2026-02-02 17:52:25 +07:00
parent 0cd5350a7b
commit e7f6e9c20a
2 changed files with 57 additions and 1 deletions

Binary file not shown.

View File

@@ -8395,6 +8395,29 @@ def set_setting(
db.commit() db.commit()
@api_router.get("/config/stripe")
async def get_stripe_public_config(db: Session = Depends(get_db)):
"""
Get Stripe publishable key for frontend (public endpoint).
This endpoint provides the publishable key needed for Stripe.js
to initialize payment forms. No authentication required since
publishable keys are meant to be public.
"""
publishable_key = get_setting(db, 'stripe_publishable_key', decrypt=False)
if not publishable_key:
raise HTTPException(
status_code=503,
detail="Stripe is not configured. Please contact the administrator."
)
return {
"publishable_key": publishable_key,
"environment": "test" if publishable_key.startswith('pk_test_') else "live"
}
@api_router.get("/admin/settings/stripe/status") @api_router.get("/admin/settings/stripe/status")
async def get_stripe_status( async def get_stripe_status(
current_user: User = Depends(get_current_superadmin), current_user: User = Depends(get_current_superadmin),
@@ -8405,6 +8428,7 @@ async def get_stripe_status(
Returns: Returns:
- configured: Whether credentials exist in database - configured: Whether credentials exist in database
- publishable_key_prefix: First 12 chars of publishable key
- secret_key_prefix: First 10 chars of secret key (for verification) - secret_key_prefix: First 10 chars of secret key (for verification)
- webhook_configured: Whether webhook secret exists - webhook_configured: Whether webhook secret exists
- environment: test or live (based on key prefix) - environment: test or live (based on key prefix)
@@ -8413,10 +8437,11 @@ async def get_stripe_status(
import os import os
# Read from database # Read from database
publishable_key = get_setting(db, 'stripe_publishable_key', decrypt=False)
secret_key = get_setting(db, 'stripe_secret_key', decrypt=True) secret_key = get_setting(db, 'stripe_secret_key', decrypt=True)
webhook_secret = get_setting(db, 'stripe_webhook_secret', decrypt=True) webhook_secret = get_setting(db, 'stripe_webhook_secret', decrypt=True)
configured = bool(secret_key) configured = bool(secret_key) and bool(publishable_key)
environment = 'unknown' environment = 'unknown'
if secret_key: if secret_key:
@@ -8436,6 +8461,8 @@ async def get_stripe_status(
return { return {
"configured": configured, "configured": configured,
"publishable_key_prefix": publishable_key[:12] if publishable_key else None,
"publishable_key_set": bool(publishable_key),
"secret_key_prefix": secret_key[:10] if secret_key else None, "secret_key_prefix": secret_key[:10] if secret_key else None,
"secret_key_set": bool(secret_key), "secret_key_set": bool(secret_key),
"webhook_secret_set": bool(webhook_secret), "webhook_secret_set": bool(webhook_secret),
@@ -8444,6 +8471,7 @@ async def get_stripe_status(
"instructions": { "instructions": {
"location": "Database (system_settings table)", "location": "Database (system_settings table)",
"required_settings": [ "required_settings": [
"stripe_publishable_key (pk_test_... or pk_live_...)",
"stripe_secret_key (sk_test_... or sk_live_...)", "stripe_secret_key (sk_test_... or sk_live_...)",
"stripe_webhook_secret (whsec_...)" "stripe_webhook_secret (whsec_...)"
], ],
@@ -8501,6 +8529,7 @@ async def test_stripe_connection(
class UpdateStripeSettingsRequest(BaseModel): class UpdateStripeSettingsRequest(BaseModel):
"""Request model for updating Stripe settings""" """Request model for updating Stripe settings"""
publishable_key: str = Field(..., min_length=1, description="Stripe publishable key (pk_test_... or pk_live_...)")
secret_key: str = Field(..., min_length=1, description="Stripe secret key (sk_test_... or sk_live_...)") secret_key: str = Field(..., min_length=1, description="Stripe secret key (sk_test_... or sk_live_...)")
webhook_secret: str = Field(..., min_length=1, description="Stripe webhook secret (whsec_...)") webhook_secret: str = Field(..., min_length=1, description="Stripe webhook secret (whsec_...)")
@@ -8517,6 +8546,13 @@ async def update_stripe_settings(
Stores Stripe credentials encrypted in the database. Stores Stripe credentials encrypted in the database.
Changes take effect immediately without server restart. Changes take effect immediately without server restart.
""" """
# Validate publishable key format
if not (request.publishable_key.startswith('pk_test_') or request.publishable_key.startswith('pk_live_')):
raise HTTPException(
status_code=400,
detail="Invalid Stripe publishable key format. Must start with 'pk_test_' or 'pk_live_'"
)
# Validate secret key format # Validate secret key format
if not (request.secret_key.startswith('sk_test_') or request.secret_key.startswith('sk_live_')): if not (request.secret_key.startswith('sk_test_') or request.secret_key.startswith('sk_live_')):
raise HTTPException( raise HTTPException(
@@ -8531,7 +8567,27 @@ async def update_stripe_settings(
detail="Invalid Stripe webhook secret format. Must start with 'whsec_'" detail="Invalid Stripe webhook secret format. Must start with 'whsec_'"
) )
# Validate key environment consistency (publishable and secret should match)
pk_is_live = request.publishable_key.startswith('pk_live_')
sk_is_live = request.secret_key.startswith('sk_live_')
if pk_is_live != sk_is_live:
raise HTTPException(
status_code=400,
detail="Publishable key and Secret key must be from the same environment (both test or both live)"
)
try: try:
# Store publishable key (NOT encrypted - it's meant to be public)
set_setting(
db=db,
key='stripe_publishable_key',
value=request.publishable_key,
user_id=str(current_user.id),
description='Stripe publishable key for frontend payment forms',
is_sensitive=False,
encrypt=False
)
# Store secret key (encrypted) # Store secret key (encrypted)
set_setting( set_setting(
db=db, db=db,