first commit

This commit is contained in:
Koncept Kit
2025-12-05 16:43:37 +07:00
commit 6ef7685ade
26 changed files with 2191 additions and 0 deletions

180
payment_service.py Normal file
View File

@@ -0,0 +1,180 @@
"""
Payment service for Stripe integration.
Handles subscription creation, checkout sessions, and webhook processing.
"""
import stripe
import os
from dotenv import load_dotenv
from datetime import datetime, timezone, timedelta
# Load environment variables
load_dotenv()
# Initialize Stripe with secret key
stripe.api_key = os.getenv("STRIPE_SECRET_KEY")
# Stripe webhook secret for signature verification
STRIPE_WEBHOOK_SECRET = os.getenv("STRIPE_WEBHOOK_SECRET")
def create_checkout_session(
user_id: str,
user_email: str,
plan_id: str,
stripe_price_id: str,
success_url: str,
cancel_url: str
):
"""
Create a Stripe Checkout session for subscription payment.
Args:
user_id: User's UUID
user_email: User's email address
plan_id: SubscriptionPlan UUID
stripe_price_id: Stripe Price ID for the plan
success_url: URL to redirect after successful payment
cancel_url: URL to redirect if user cancels
Returns:
dict: Checkout session object with session ID and URL
"""
try:
# Create Checkout Session
checkout_session = stripe.checkout.Session.create(
customer_email=user_email,
payment_method_types=["card"],
line_items=[
{
"price": stripe_price_id,
"quantity": 1,
}
],
mode="subscription",
success_url=success_url,
cancel_url=cancel_url,
metadata={
"user_id": str(user_id),
"plan_id": str(plan_id),
},
subscription_data={
"metadata": {
"user_id": str(user_id),
"plan_id": str(plan_id),
}
}
)
return {
"session_id": checkout_session.id,
"url": checkout_session.url
}
except stripe.error.StripeError as e:
raise Exception(f"Stripe error: {str(e)}")
def verify_webhook_signature(payload: bytes, sig_header: str) -> dict:
"""
Verify Stripe webhook signature and construct event.
Args:
payload: Raw webhook payload bytes
sig_header: Stripe signature header
Returns:
dict: Verified webhook event
Raises:
ValueError: If signature verification fails
"""
try:
event = stripe.Webhook.construct_event(
payload, sig_header, STRIPE_WEBHOOK_SECRET
)
return event
except ValueError as e:
raise ValueError(f"Invalid payload: {str(e)}")
except stripe.error.SignatureVerificationError as e:
raise ValueError(f"Invalid signature: {str(e)}")
def get_subscription_end_date(billing_cycle: str = "yearly") -> datetime:
"""
Calculate subscription end date based on billing cycle.
Args:
billing_cycle: "yearly" or "monthly"
Returns:
datetime: End date for the subscription
"""
now = datetime.now(timezone.utc)
if billing_cycle == "yearly":
# Add 1 year
return now + timedelta(days=365)
elif billing_cycle == "monthly":
# Add 1 month (approximation)
return now + timedelta(days=30)
else:
# Default to yearly
return now + timedelta(days=365)
def create_stripe_price(
product_name: str,
price_cents: int,
billing_cycle: str = "yearly"
) -> str:
"""
Create a Stripe Price object for a subscription plan.
Args:
product_name: Name of the product/plan
price_cents: Price in cents
billing_cycle: "yearly" or "monthly"
Returns:
str: Stripe Price ID
"""
try:
# Create a product first
product = stripe.Product.create(name=product_name)
# Determine recurring interval
interval = "year" if billing_cycle == "yearly" else "month"
# Create price
price = stripe.Price.create(
product=product.id,
unit_amount=price_cents,
currency="usd",
recurring={"interval": interval},
)
return price.id
except stripe.error.StripeError as e:
raise Exception(f"Stripe error creating price: {str(e)}")
def get_customer_portal_url(stripe_customer_id: str, return_url: str) -> str:
"""
Create a Stripe Customer Portal session for subscription management.
Args:
stripe_customer_id: Stripe Customer ID
return_url: URL to return to after portal session
Returns:
str: Customer portal URL
"""
try:
session = stripe.billing_portal.Session.create(
customer=stripe_customer_id,
return_url=return_url,
)
return session.url
except stripe.error.StripeError as e:
raise Exception(f"Stripe error creating portal session: {str(e)}")