feat: use OAuth for stripe connect

This commit is contained in:
swve 2024-11-18 18:18:34 +01:00
parent 93c0838fab
commit 0449d6f87c
9 changed files with 266 additions and 50 deletions

View file

@ -18,7 +18,7 @@ from src.services.payments.payments_courses import (
get_courses_by_product,
)
from src.services.payments.payments_users import get_owned_courses
from src.services.payments.payments_stripe import create_checkout_session, update_stripe_account_id
from src.services.payments.payments_stripe import create_checkout_session, handle_stripe_oauth_callback, update_stripe_account_id
from src.services.payments.payments_access import check_course_paid_access
from src.services.payments.payments_customers import get_customers
from src.services.payments.payments_stripe import generate_stripe_connect_link
@ -165,7 +165,14 @@ async def api_handle_connected_accounts_stripe_webhook(
request: Request,
db_session: Session = Depends(get_db_session),
):
return await handle_stripe_webhook(request, db_session)
return await handle_stripe_webhook(request, "standard", db_session)
@router.post("/stripe/webhook/connect")
async def api_handle_connected_accounts_stripe_webhook_connect(
request: Request,
db_session: Session = Depends(get_db_session),
):
return await handle_stripe_webhook(request, "connect", db_session)
# Payments checkout
@ -246,3 +253,13 @@ async def api_generate_stripe_connect_link(
return await generate_stripe_connect_link(
request, org_id, redirect_uri, current_user, db_session
)
@router.get("/stripe/oauth/callback")
async def stripe_oauth_callback(
request: Request,
code: str,
org_id: int,
current_user: PublicUser = Depends(get_current_user),
db_session: Session = Depends(get_db_session),
):
return await handle_stripe_oauth_callback(request, org_id, code, current_user, db_session)

View file

@ -52,7 +52,8 @@ async def get_stripe_internal_credentials(
return {
"stripe_secret_key": learnhouse_config.payments_config.stripe.stripe_secret_key,
"stripe_publishable_key": learnhouse_config.payments_config.stripe.stripe_publishable_key,
"stripe_webhook_secret": learnhouse_config.payments_config.stripe.stripe_webhook_secret,
"stripe_webhook_standard_secret": learnhouse_config.payments_config.stripe.stripe_webhook_standard_secret,
"stripe_webhook_connect_secret": learnhouse_config.payments_config.stripe.stripe_webhook_connect_secret,
}
@ -332,30 +333,20 @@ async def generate_stripe_connect_link(
# Get credentials
creds = await get_stripe_internal_credentials()
stripe.api_key = creds.get("stripe_secret_key")
# Get learnhouse config for client_id
learnhouse_config = get_learnhouse_config()
client_id = learnhouse_config.payments_config.stripe.stripe_client_id
if not client_id:
raise HTTPException(status_code=400, detail="Stripe client ID not configured")
try:
# Try to get existing account ID
stripe_acc_id = await get_stripe_connected_account_id(request, org_id, current_user, db_session)
except HTTPException:
# If no account exists, create one
stripe_account = await create_stripe_account(
request,
org_id,
"standard",
current_user,
db_session
)
stripe_acc_id = stripe_account
state = f"org_id={org_id}"
# Generate OAuth link for existing accounts
oauth_link = f"https://connect.stripe.com/oauth/authorize?response_type=code&client_id={client_id}&scope=read_write&redirect_uri={redirect_uri}&state={state}"
# Generate OAuth link
connect_link = stripe.AccountLink.create(
account=str(stripe_acc_id),
type="account_onboarding",
return_url=redirect_uri,
refresh_url=redirect_uri,
)
return {"connect_url": connect_link.url}
return {"connect_url": oauth_link}
async def create_stripe_account(
request: Request,
@ -438,3 +429,45 @@ async def update_stripe_account_id(
)
return {"message": "Stripe account ID updated successfully"}
async def handle_stripe_oauth_callback(
request: Request,
org_id: int,
code: str,
current_user: PublicUser | AnonymousUser | InternalUser,
db_session: Session,
):
"""
Handle the OAuth callback from Stripe and complete the account connection
"""
creds = await get_stripe_internal_credentials()
stripe.api_key = creds.get("stripe_secret_key")
try:
# Exchange the authorization code for an access token
response = stripe.OAuth.token(
grant_type='authorization_code',
code=code,
)
connected_account_id = response.stripe_user_id
if not connected_account_id:
raise HTTPException(status_code=400, detail="No account ID received from Stripe")
# Now connected_account_id is guaranteed to be a string
await update_stripe_account_id(
request,
org_id,
connected_account_id,
current_user,
db_session,
)
return {"success": True, "account_id": connected_account_id}
except stripe.StripeError as e:
logging.error(f"Error connecting Stripe account: {str(e)}")
raise HTTPException(
status_code=400,
detail=f"Error connecting Stripe account: {str(e)}"
)

View file

@ -1,3 +1,4 @@
from typing import Literal
from fastapi import HTTPException, Request
from sqlmodel import Session, select
import stripe
@ -15,11 +16,12 @@ logger = logging.getLogger(__name__)
async def handle_stripe_webhook(
request: Request,
webhook_type: Literal["connect", "standard"],
db_session: Session,
) -> dict:
# Get Stripe credentials
creds = await get_stripe_internal_credentials()
webhook_secret = creds.get('stripe_webhook_secret')
webhook_secret = creds.get(f'stripe_webhook_{webhook_type}_secret')
stripe.api_key = creds.get("stripe_secret_key")
if not webhook_secret: