forked from andika/membership-be
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:
Binary file not shown.
58
server.py
58
server.py
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user