AkashKumarave commited on
Commit
3d31d25
·
verified ·
1 Parent(s): 8b5f7dc

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +177 -132
app.py CHANGED
@@ -2,35 +2,36 @@ import logging
2
  from fastapi import FastAPI, UploadFile, File, HTTPException, Form, Request
3
  from fastapi.middleware.cors import CORSMiddleware
4
  from fastapi.responses import FileResponse, JSONResponse
5
- import google.generativeai as genai
6
  import base64
7
  import os
 
 
8
  from pathlib import Path
9
  from typing import List
10
  import io
11
- from PIL import Image
12
  import razorpay
13
  from razorpay.errors import SignatureVerificationError
14
  from supabase import create_client, Client
15
  from pydantic import BaseModel
16
  from typing import Optional
17
- import time
18
 
19
  # Configure logging
20
  logging.basicConfig(level=logging.INFO)
21
  logger = logging.getLogger(__name__)
22
 
23
  # Initialize FastAPI app
24
- app = FastAPI(title="Gemini Image Generator API with Razorpay")
25
 
26
- # Enable CORS for the frontend
27
  app.add_middleware(
28
  CORSMiddleware,
29
  allow_origins=[
30
- "https://hivili.web.app",
31
  "http://localhost:3000",
32
  "https://*.lovable.dev",
33
  "https://*.sandbox.lovable.dev",
 
34
  ],
35
  allow_origin_regex=r"https://.*\.lovable\.dev|https://.*\.sandbox\.lovable\.dev",
36
  allow_credentials=True,
@@ -39,18 +40,15 @@ app.add_middleware(
39
  )
40
 
41
  # ===== API CONFIGURATION =====
42
- GEMINI_API_KEY = os.getenv("AIzaSyDL5Rilo7ptJpUOZdY6wy8PJYUcVcnDADs")
43
- if not GEMINI_API_KEY:
44
- logger.error("GEMINI_API_KEY is not set")
45
- raise ValueError("GEMINI_API_KEY environment variable is not set")
46
-
47
- genai.configure(api_key=GEMINI_API_KEY)
48
- MODEL_NAME = "gemini-1.5-flash" # Use a valid model (verify in Google's documentation)
49
 
50
  # ===== RAZORPAY CONFIGURATION =====
51
  RAZORPAY_KEY_ID = os.getenv("RAZORPAY_KEY_ID")
52
  RAZORPAY_KEY_SECRET = os.getenv("RAZORPAY_KEY_SECRET")
53
- razorpay_client = razorpay.Client(auth=(RAZORPAY_KEY_ID, RAZORPAY_KEY_SECRET)) if RAZORPAY_KEY_ID and RAZORPAY_KEY_SECRET else None
54
 
55
  # ===== SUPABASE CONFIGURATION =====
56
  SUPABASE_URL = os.getenv("SUPABASE_URL")
@@ -67,92 +65,158 @@ class VerifyPaymentRequest(BaseModel):
67
  razorpay_signature: str
68
  user_id: Optional[str] = None
69
 
 
 
 
 
 
 
 
 
 
 
70
  # ===== IMAGE PROCESSING =====
 
 
 
 
 
 
 
 
71
  def validate_image(image_content: bytes):
72
  """Validate image meets API requirements"""
73
  try:
74
  size_mb = len(image_content) / (1024 * 1024)
75
- if size_mb > 20:
76
- raise HTTPException(status_code=400, detail="Image too large (max 20MB)")
77
- img = Image.open(io.BytesIO(image_content))
78
- if img.format not in ["PNG", "JPEG"]:
79
- raise HTTPException(status_code=400, detail="Only PNG or JPEG images are supported")
80
- logger.info(f"Validated image: format={img.format}, size={size_mb:.2f}MB")
81
- return True, img.format.lower()
82
  except Exception as e:
83
- logger.error(f"Image validation error: {str(e)}")
84
  raise HTTPException(status_code=400, detail=f"Image validation error: {str(e)}")
85
 
86
  # ===== API FUNCTIONS =====
87
  def create_multi_image_task(subject_images: List[bytes], prompt: str):
88
- """Create image generation task with Gemini API (up to two images)"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
  try:
90
- model = genai.GenerativeModel(MODEL_NAME)
91
- parts = []
92
- for img_content in subject_images:
93
- _, img_format = validate_image(img_content)
94
- parts.append({
95
- "inline_data": {
96
- "data": base64.b64encode(img_content).decode('utf-8'),
97
- "mime_type": f"image/{img_format}"
98
- }
99
- })
100
- enhanced_prompt = f"A photorealistic composition combining elements from the provided images: {prompt}. Ensure the scene is cohesive, with soft, natural lighting and a balanced aspect ratio of 16:9."
101
- parts.append({"text": enhanced_prompt})
102
-
103
- logger.info(f"Sending request to Gemini API with prompt: {prompt}")
104
- response = model.generate_content(
105
- parts,
106
- generation_config={"response_mime_type": "image/png"},
107
- safety_settings=[
108
- {"category": "HARM_CATEGORY_DANGEROUS_CONTENT", "threshold": "BLOCK_NONE"},
109
- {"category": "HARM_CATEGORY_HATE_SPEECH", "threshold": "BLOCK_NONE"},
110
- {"category": "HARM_CATEGORY_SEXUALLY_EXPLICIT", "threshold": "BLOCK_NONE"},
111
- {"category": "HARM_CATEGORY_HARASSMENT", "threshold": "BLOCK_NONE"}
112
- ]
113
- )
114
- logger.info(f"API response: {response}")
115
- if not response.candidates or not response.candidates[0].content:
116
- raise HTTPException(status_code=500, detail="No valid content returned from API")
117
- return response
118
- except Exception as e:
119
  logger.error(f"API request failed: {str(e)}")
 
 
120
  raise HTTPException(status_code=500, detail=f"API Error: {str(e)}")
121
 
122
- async def generate_image(subject_images: List[bytes], prompt: str):
123
- """Handle complete image generation workflow"""
124
- if len(subject_images) != 2:
125
- raise HTTPException(status_code=400, detail="Exactly two images are required")
126
-
127
- for img_content in subject_images:
128
- validate_image(img_content)
129
 
130
- response = create_multi_image_task(subject_images, prompt)
131
  try:
132
- candidate = response.candidates[0]
133
- parts = candidate.content.parts
134
- logger.info(f"Response parts: {parts}")
 
 
135
 
136
- image_data = None
137
- for part in parts:
138
- if hasattr(part, 'inline_data') and part.inline_data.data:
139
- image_data = part.inline_data.data
140
- break
141
- elif hasattr(part, 'text'):
142
- logger.info(f"Text part found: {part.text}")
143
-
144
- if not image_data:
145
- raise HTTPException(status_code=500, detail="No image data found in API response")
 
 
 
 
 
 
 
146
 
147
- output_dir = Path("/tmp")
148
- output_dir.mkdir(exist_ok=True)
149
- output_path = output_dir / f"gemini_output_{int(time.time())}.png"
150
- with open(output_path, "wb") as f:
151
- f.write(image_data)
152
- return output_path
 
 
 
 
 
 
 
 
153
  except Exception as e:
154
- logger.error(f"Failed to process API response: {str(e)}")
155
- raise HTTPException(status_code=500, detail=f"Failed to process result: {str(e)}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
156
 
157
  # ===== API ENDPOINTS =====
158
  @app.post("/generate")
@@ -160,16 +224,20 @@ async def generate_image_endpoint(
160
  prompt: str = Form(...),
161
  images: List[UploadFile] = File(...)
162
  ):
163
- """Endpoint to generate an image from exactly two input images and a prompt"""
164
  try:
165
- if len(images) != 2:
166
- raise HTTPException(status_code=400, detail="Exactly two images are required")
 
 
 
167
  image_contents = [await image.read() for image in images]
168
  output_path = await generate_image(image_contents, prompt)
 
169
  return FileResponse(
170
  path=output_path,
171
  media_type="image/png",
172
- filename=f"gemini_output_{Path(output_path).stem}.png"
173
  )
174
  except Exception as e:
175
  logger.error(f"Error in /generate: {str(e)}")
@@ -183,33 +251,39 @@ async def create_order_endpoint(
183
  ):
184
  """Create a Razorpay order (supports form-data and JSON)"""
185
  logger.info("Received create order request")
 
186
  if not RAZORPAY_KEY_ID or not RAZORPAY_KEY_SECRET:
187
  logger.error("Razorpay configuration missing")
188
  raise HTTPException(status_code=500, detail="Razorpay configuration missing")
189
 
 
190
  try:
191
  if body and body.amount:
192
  amount = body.amount
193
  elif not amount:
 
194
  try:
195
  json_body = await request.json()
196
  amount = json_body.get('amount')
197
  except:
198
  pass
199
-
200
  if not amount or amount <= 0:
201
  raise HTTPException(status_code=422, detail="Missing or invalid 'amount' parameter")
202
-
203
  logger.info(f"Creating order with amount: {amount}")
204
  order = create_razorpay_order(amount)
 
205
  response_data = {
206
  "id": order["id"],
207
  "amount": order["amount"],
208
  "currency": order["currency"],
209
  "key_id": RAZORPAY_KEY_ID
210
  }
 
211
  logger.info(f"Order created successfully: {order['id']}")
212
  return JSONResponse(content=response_data)
 
213
  except HTTPException:
214
  raise
215
  except Exception as e:
@@ -227,13 +301,16 @@ async def verify_payment_endpoint(
227
  ):
228
  """Verify Razorpay payment signature (supports form-data and JSON)"""
229
  logger.info("Received payment verification request")
 
230
  try:
 
231
  if body:
232
  razorpay_order_id = razorpay_order_id or body.razorpay_order_id
233
  razorpay_payment_id = razorpay_payment_id or body.razorpay_payment_id
234
  razorpay_signature = razorpay_signature or body.razorpay_signature
235
  user_id = user_id or body.user_id
236
  else:
 
237
  try:
238
  json_body = await request.json()
239
  razorpay_order_id = razorpay_order_id or json_body.get('razorpay_order_id')
@@ -242,23 +319,23 @@ async def verify_payment_endpoint(
242
  user_id = user_id or json_body.get('user_id')
243
  except:
244
  pass
245
-
 
246
  if not all([razorpay_order_id, razorpay_payment_id, razorpay_signature]):
247
  missing_fields = []
248
- if not razorpay_order_id:
249
- missing_fields.append("razorpay_order_id")
250
- if not razorpay_payment_id:
251
- missing_fields.append("razorpay_payment_id")
252
- if not razorpay_signature:
253
- missing_fields.append("razorpay_signature")
254
  logger.error(f"Missing required fields: {missing_fields}")
255
  raise HTTPException(
256
- status_code=422,
257
  detail=f"Missing required fields: {', '.join(missing_fields)}"
258
  )
259
-
260
  logger.info(f"Verifying payment for order_id: {razorpay_order_id}")
261
  is_valid = verify_payment_signature(razorpay_order_id, razorpay_payment_id, razorpay_signature)
 
262
  if is_valid:
263
  if user_id and supabase:
264
  logger.info(f"Updating Supabase for user_id: {user_id}")
@@ -267,10 +344,13 @@ async def verify_payment_endpoint(
267
  logger.info(f"Successfully updated premium status for user: {user_id}")
268
  except Exception as e:
269
  logger.error(f"Failed to update Supabase: {str(e)}")
 
 
270
  return JSONResponse(content={"success": True, "message": "Payment verified successfully"})
271
  else:
272
  logger.warning(f"Payment verification failed for order: {razorpay_order_id}")
273
  return JSONResponse(content={"success": False, "message": "Payment verification failed"}, status_code=400)
 
274
  except HTTPException:
275
  raise
276
  except Exception as e:
@@ -280,7 +360,7 @@ async def verify_payment_endpoint(
280
  @app.get("/")
281
  async def index():
282
  return {
283
- "status": "Gemini Image Generator API with Razorpay is running",
284
  "endpoints": {
285
  "generate": "POST /generate",
286
  "create_order": "POST /create-razorpay-order",
@@ -296,41 +376,6 @@ async def health_check():
296
  "supabase_configured": bool(SUPABASE_URL and SUPABASE_KEY)
297
  }
298
 
299
- def create_razorpay_order(amount: int):
300
- """Create a Razorpay order"""
301
- try:
302
- if amount <= 0:
303
- raise ValueError("Amount must be a positive integer")
304
- order_data = {
305
- "amount": amount * 100, # Convert INR to paise
306
- "currency": "INR",
307
- "payment_capture": 1 # Auto-capture payment
308
- }
309
- order = razorpay_client.order.create(data=order_data)
310
- logger.info(f"Razorpay order created successfully: {order['id']}")
311
- return order
312
- except Exception as e:
313
- logger.error(f"Failed to create Razorpay order: {str(e)}")
314
- raise HTTPException(status_code=500, detail=f"Failed to create order: {str(e)}")
315
-
316
- def verify_payment_signature(order_id: str, payment_id: str, signature: str):
317
- """Verify Razorpay payment signature"""
318
- try:
319
- params_dict = {
320
- "razorpay_order_id": order_id,
321
- "razorpay_payment_id": payment_id,
322
- "razorpay_signature": signature
323
- }
324
- razorpay_client.utility.verify_payment_signature(params_dict)
325
- logger.info(f"Payment signature verified successfully for order: {order_id}")
326
- return True
327
- except SignatureVerificationError as e:
328
- logger.error(f"Payment signature verification failed: {str(e)}")
329
- return False
330
- except Exception as e:
331
- logger.error(f"Error verifying payment signature: {str(e)}")
332
- raise HTTPException(status_code=500, detail=f"Verification error: {str(e)}")
333
-
334
  if __name__ == "__main__":
335
  import uvicorn
336
  uvicorn.run(app, host="0.0.0.0", port=7860)
 
2
  from fastapi import FastAPI, UploadFile, File, HTTPException, Form, Request
3
  from fastapi.middleware.cors import CORSMiddleware
4
  from fastapi.responses import FileResponse, JSONResponse
5
+ import requests
6
  import base64
7
  import os
8
+ import time
9
+ import jwt
10
  from pathlib import Path
11
  from typing import List
12
  import io
 
13
  import razorpay
14
  from razorpay.errors import SignatureVerificationError
15
  from supabase import create_client, Client
16
  from pydantic import BaseModel
17
  from typing import Optional
 
18
 
19
  # Configure logging
20
  logging.basicConfig(level=logging.INFO)
21
  logger = logging.getLogger(__name__)
22
 
23
  # Initialize FastAPI app
24
+ app = FastAPI(title="Kling AI Multi-Image Generator API with Razorpay")
25
 
26
+ # Enable CORS for the frontend - Updated for Lovable domains
27
  app.add_middleware(
28
  CORSMiddleware,
29
  allow_origins=[
30
+ "https://hivili.web.app",
31
  "http://localhost:3000",
32
  "https://*.lovable.dev",
33
  "https://*.sandbox.lovable.dev",
34
+ # Add pattern matching for Lovable subdomains
35
  ],
36
  allow_origin_regex=r"https://.*\.lovable\.dev|https://.*\.sandbox\.lovable\.dev",
37
  allow_credentials=True,
 
40
  )
41
 
42
  # ===== API CONFIGURATION =====
43
+ ACCESS_KEY_ID = os.getenv("ACCESS_KEY_ID", "AFyHfnQATghFdCMyAG3gRPbNY4TNKFGB")
44
+ ACCESS_KEY_SECRET = os.getenv("ACCESS_KEY_SECRET", "TTepeLyBterLNM3brYPGmdndBnnyKJBA")
45
+ API_BASE_URL = "https://api-singapore.klingai.com"
46
+ CREATE_TASK_ENDPOINT = f"{API_BASE_URL}/v1/images/multi-image2image"
 
 
 
47
 
48
  # ===== RAZORPAY CONFIGURATION =====
49
  RAZORPAY_KEY_ID = os.getenv("RAZORPAY_KEY_ID")
50
  RAZORPAY_KEY_SECRET = os.getenv("RAZORPAY_KEY_SECRET")
51
+ razorpay_client = razorpay.Client(auth=(RAZORPAY_KEY_ID, RAZORPAY_KEY_SECRET))
52
 
53
  # ===== SUPABASE CONFIGURATION =====
54
  SUPABASE_URL = os.getenv("SUPABASE_URL")
 
65
  razorpay_signature: str
66
  user_id: Optional[str] = None
67
 
68
+ # ===== AUTHENTICATION =====
69
+ def generate_jwt_token():
70
+ """Generate JWT token for API authentication"""
71
+ payload = {
72
+ "iss": ACCESS_KEY_ID,
73
+ "exp": int(time.time()) + 1800, # 30 minutes expiration
74
+ "nbf": int(time.time()) - 5 # Not before 5 seconds ago
75
+ }
76
+ return jwt.encode(payload, ACCESS_KEY_SECRET, algorithm="HS256")
77
+
78
  # ===== IMAGE PROCESSING =====
79
+ def prepare_image_base64(image_content: bytes):
80
+ """Convert image bytes to base64 without prefix"""
81
+ try:
82
+ return base64.b64encode(image_content).decode('utf-8')
83
+ except Exception as e:
84
+ logger.error(f"Image processing failed: {str(e)}")
85
+ raise HTTPException(status_code=500, detail=f"Image processing failed: {str(e)}")
86
+
87
  def validate_image(image_content: bytes):
88
  """Validate image meets API requirements"""
89
  try:
90
  size_mb = len(image_content) / (1024 * 1024)
91
+ if size_mb > 10:
92
+ raise HTTPException(status_code=400, detail="Image too large (max 10MB)")
93
+ return True, ""
 
 
 
 
94
  except Exception as e:
 
95
  raise HTTPException(status_code=400, detail=f"Image validation error: {str(e)}")
96
 
97
  # ===== API FUNCTIONS =====
98
  def create_multi_image_task(subject_images: List[bytes], prompt: str):
99
+ """Create multi-image generation task"""
100
+ headers = {
101
+ "Authorization": f"Bearer {generate_jwt_token()}",
102
+ "Content-Type": "application/json"
103
+ }
104
+
105
+ subject_image_list = []
106
+ for img_content in subject_images:
107
+ if img_content:
108
+ base64_img = prepare_image_base64(img_content)
109
+ if base64_img:
110
+ subject_image_list.append({"subject_image": base64_img})
111
+
112
+ if len(subject_image_list) < 2:
113
+ raise HTTPException(status_code=400, detail="At least 2 subject images required")
114
+
115
+ payload = {
116
+ "model_name": "kling-v2",
117
+ "prompt": prompt,
118
+ "subject_image_list": subject_image_list,
119
+ "n": 1,
120
+ "aspect_ratio": "1:1"
121
+ }
122
+
123
  try:
124
+ response = requests.post(CREATE_TASK_ENDPOINT, json=payload, headers=headers)
125
+ response.raise_for_status()
126
+ return response.json()
127
+ except requests.exceptions.RequestException as e:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
128
  logger.error(f"API request failed: {str(e)}")
129
+ if hasattr(e, 'response') and e.response:
130
+ logger.error(f"API response: {e.response.text}")
131
  raise HTTPException(status_code=500, detail=f"API Error: {str(e)}")
132
 
133
+ def check_task_status(task_id: str):
134
+ """Check task completion status"""
135
+ headers = {"Authorization": f"Bearer {generate_jwt_token()}"}
136
+ status_url = f"{API_BASE_URL}/v1/images/multi-image2image/{task_id}"
 
 
 
137
 
 
138
  try:
139
+ response = requests.get(status_url, headers=headers)
140
+ response.raise_for_status()
141
+ return response.json()
142
+ except requests.exceptions.RequestException as e:
143
+ raise HTTPException(status_code=500, detail=f"Status check failed: {str(e)}")
144
 
145
+ # ===== RAZORPAY FUNCTIONS =====
146
+ def create_razorpay_order(amount: int):
147
+ """Create a Razorpay order"""
148
+ try:
149
+ if amount <= 0:
150
+ raise ValueError("Amount must be a positive integer")
151
+ order_data = {
152
+ "amount": amount * 100, # Convert INR to paise
153
+ "currency": "INR",
154
+ "payment_capture": 1 # Auto-capture payment
155
+ }
156
+ order = razorpay_client.order.create(data=order_data)
157
+ logger.info(f"Razorpay order created successfully: {order['id']}")
158
+ return order
159
+ except Exception as e:
160
+ logger.error(f"Failed to create Razorpay order: {str(e)}")
161
+ raise HTTPException(status_code=500, detail=f"Failed to create order: {str(e)}")
162
 
163
+ def verify_payment_signature(order_id: str, payment_id: str, signature: str):
164
+ """Verify Razorpay payment signature"""
165
+ try:
166
+ params_dict = {
167
+ "razorpay_order_id": order_id,
168
+ "razorpay_payment_id": payment_id,
169
+ "razorpay_signature": signature
170
+ }
171
+ razorpay_client.utility.verify_payment_signature(params_dict)
172
+ logger.info(f"Payment signature verified successfully for order: {order_id}")
173
+ return True
174
+ except SignatureVerificationError as e:
175
+ logger.error(f"Payment signature verification failed: {str(e)}")
176
+ return False
177
  except Exception as e:
178
+ logger.error(f"Error verifying payment signature: {str(e)}")
179
+ raise HTTPException(status_code=500, detail=f"Verification error: {str(e)}")
180
+
181
+ # ===== MAIN PROCESSING =====
182
+ async def generate_image(subject_images: List[bytes], prompt: str):
183
+ """Handle complete image generation workflow"""
184
+ for img_content in subject_images:
185
+ if img_content:
186
+ validate_image(img_content)
187
+
188
+ task_response = create_multi_image_task(subject_images, prompt)
189
+ if task_response.get("code") != 0:
190
+ raise HTTPException(status_code=500, detail=f"API error: {task_response.get('message', 'Unknown error')}")
191
+
192
+ task_id = task_response["data"]["task_id"]
193
+ logger.info(f"Task created: {task_id}")
194
+
195
+ for _ in range(60):
196
+ task_data = check_task_status(task_id)
197
+ status = task_data["data"]["task_status"]
198
+
199
+ if status == "succeed":
200
+ image_url = task_data["data"]["task_result"]["images"][0]["url"]
201
+ try:
202
+ response = requests.get(image_url)
203
+ response.raise_for_status()
204
+ output_dir = Path("/tmp")
205
+ output_dir.mkdir(exist_ok=True)
206
+ output_path = output_dir / f"kling_output_{task_id}.png"
207
+ with open(output_path, "wb") as f:
208
+ f.write(response.content)
209
+ return output_path
210
+ except Exception as e:
211
+ raise HTTPException(status_code=500, detail=f"Failed to download result: {str(e)}")
212
+
213
+ elif status in ("failed", "canceled"):
214
+ error_msg = task_data["data"].get("task_status_msg", "Unknown error")
215
+ raise HTTPException(status_code=500, detail=f"Task failed: {error_msg}")
216
+
217
+ time.sleep(10)
218
+
219
+ raise HTTPException(status_code=500, detail="Task timed out after 10 minutes")
220
 
221
  # ===== API ENDPOINTS =====
222
  @app.post("/generate")
 
224
  prompt: str = Form(...),
225
  images: List[UploadFile] = File(...)
226
  ):
227
+ """Endpoint to generate an image from multiple input images and a prompt"""
228
  try:
229
+ if len(images) < 2:
230
+ raise HTTPException(status_code=400, detail="At least 2 images are required")
231
+ if len(images) > 4:
232
+ raise HTTPException(status_code=400, detail="Maximum 4 images allowed")
233
+
234
  image_contents = [await image.read() for image in images]
235
  output_path = await generate_image(image_contents, prompt)
236
+
237
  return FileResponse(
238
  path=output_path,
239
  media_type="image/png",
240
+ filename=f"kling_output_{Path(output_path).stem}.png"
241
  )
242
  except Exception as e:
243
  logger.error(f"Error in /generate: {str(e)}")
 
251
  ):
252
  """Create a Razorpay order (supports form-data and JSON)"""
253
  logger.info("Received create order request")
254
+
255
  if not RAZORPAY_KEY_ID or not RAZORPAY_KEY_SECRET:
256
  logger.error("Razorpay configuration missing")
257
  raise HTTPException(status_code=500, detail="Razorpay configuration missing")
258
 
259
+ # Handle JSON body if provided
260
  try:
261
  if body and body.amount:
262
  amount = body.amount
263
  elif not amount:
264
+ # Try to parse JSON from request body
265
  try:
266
  json_body = await request.json()
267
  amount = json_body.get('amount')
268
  except:
269
  pass
270
+
271
  if not amount or amount <= 0:
272
  raise HTTPException(status_code=422, detail="Missing or invalid 'amount' parameter")
273
+
274
  logger.info(f"Creating order with amount: {amount}")
275
  order = create_razorpay_order(amount)
276
+
277
  response_data = {
278
  "id": order["id"],
279
  "amount": order["amount"],
280
  "currency": order["currency"],
281
  "key_id": RAZORPAY_KEY_ID
282
  }
283
+
284
  logger.info(f"Order created successfully: {order['id']}")
285
  return JSONResponse(content=response_data)
286
+
287
  except HTTPException:
288
  raise
289
  except Exception as e:
 
301
  ):
302
  """Verify Razorpay payment signature (supports form-data and JSON)"""
303
  logger.info("Received payment verification request")
304
+
305
  try:
306
+ # Handle JSON body if provided
307
  if body:
308
  razorpay_order_id = razorpay_order_id or body.razorpay_order_id
309
  razorpay_payment_id = razorpay_payment_id or body.razorpay_payment_id
310
  razorpay_signature = razorpay_signature or body.razorpay_signature
311
  user_id = user_id or body.user_id
312
  else:
313
+ # Try to parse JSON from request body
314
  try:
315
  json_body = await request.json()
316
  razorpay_order_id = razorpay_order_id or json_body.get('razorpay_order_id')
 
319
  user_id = user_id or json_body.get('user_id')
320
  except:
321
  pass
322
+
323
+ # Validate required fields
324
  if not all([razorpay_order_id, razorpay_payment_id, razorpay_signature]):
325
  missing_fields = []
326
+ if not razorpay_order_id: missing_fields.append("razorpay_order_id")
327
+ if not razorpay_payment_id: missing_fields.append("razorpay_payment_id")
328
+ if not razorpay_signature: missing_fields.append("razorpay_signature")
329
+
 
 
330
  logger.error(f"Missing required fields: {missing_fields}")
331
  raise HTTPException(
332
+ status_code=422,
333
  detail=f"Missing required fields: {', '.join(missing_fields)}"
334
  )
335
+
336
  logger.info(f"Verifying payment for order_id: {razorpay_order_id}")
337
  is_valid = verify_payment_signature(razorpay_order_id, razorpay_payment_id, razorpay_signature)
338
+
339
  if is_valid:
340
  if user_id and supabase:
341
  logger.info(f"Updating Supabase for user_id: {user_id}")
 
344
  logger.info(f"Successfully updated premium status for user: {user_id}")
345
  except Exception as e:
346
  logger.error(f"Failed to update Supabase: {str(e)}")
347
+ # Don't fail the payment verification if Supabase update fails
348
+
349
  return JSONResponse(content={"success": True, "message": "Payment verified successfully"})
350
  else:
351
  logger.warning(f"Payment verification failed for order: {razorpay_order_id}")
352
  return JSONResponse(content={"success": False, "message": "Payment verification failed"}, status_code=400)
353
+
354
  except HTTPException:
355
  raise
356
  except Exception as e:
 
360
  @app.get("/")
361
  async def index():
362
  return {
363
+ "status": "Kling AI Multi-Image Generator API with Razorpay is running",
364
  "endpoints": {
365
  "generate": "POST /generate",
366
  "create_order": "POST /create-razorpay-order",
 
376
  "supabase_configured": bool(SUPABASE_URL and SUPABASE_KEY)
377
  }
378
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
379
  if __name__ == "__main__":
380
  import uvicorn
381
  uvicorn.run(app, host="0.0.0.0", port=7860)