Payments

class SharedPaymentMethodModel:
    """Share payment methods from platform customer to connected accounts"""
    
    def attach_platform_payment_method_to_account(self, 
                                                 user_id: str, 
                                                 connected_account_id: str, 
                                                 payment_method_id: str):
        """Clone payment method to connected account"""
        
        try:
            # Get payment method details from platform
            pm = self.client.v1.payment_methods.retrieve(payment_method_id)
            
            # Create equivalent payment method on connected account
            if pm.type == "card":
                # Note: This requires raw card data access OR use setup intents
                connected_pm = self.create_equivalent_payment_method(
                    pm, connected_account_id
                )
                
                return connected_pm.id
            
        except Exception as e:
            print(f"Failed to share payment method: {e}")
            return None
    
    def process_payment_with_shared_method(self, 
                                         user_id: str, 
                                         connected_account_id: str, 
                                         amount: int, 
                                         platform_payment_method_id: str):
        """Process payment using shared payment method"""
        
        # Get or create linked customer
        linked_customer_id = self.link_to_connected_account(user_id, connected_account_id)
        
        # Clone payment method to connected account if needed
        connected_pm_id = self.get_or_clone_payment_method(
            platform_payment_method_id, connected_account_id
        )
        
        # Process payment on connected account
        intent = self.client.v1.payment_intents.create(
            params={
                "amount": amount,
                "currency": "usd",
                "customer": linked_customer_id,
                "payment_method": connected_pm_id,
                "confirm": True
            },
            options={"stripe_account": connected_account_id}
        )
        
        return intent

class SharedPaymentMethodModel:
    """Share payment methods from platform customer to connected accounts"""
    
    def attach_platform_payment_method_to_account(self, 
                                                 user_id: str, 
                                                 connected_account_id: str, 
                                                 payment_method_id: str):
        """Clone payment method to connected account"""
        
        try:
            # Get payment method details from platform
            pm = self.client.v1.payment_methods.retrieve(payment_method_id)
            
            # Create equivalent payment method on connected account
            if pm.type == "card":
                # Note: This requires raw card data access OR use setup intents
                connected_pm = self.create_equivalent_payment_method(
                    pm, connected_account_id
                )
                
                return connected_pm.id
            
        except Exception as e:
            print(f"Failed to share payment method: {e}")
            return None
    
    def process_payment_with_shared_method(self, 
                                         user_id: str, 
                                         connected_account_id: str, 
                                         amount: int, 
                                         platform_payment_method_id: str):
        """Process payment using shared payment method"""
        
        # Get or create linked customer
        linked_customer_id = self.link_to_connected_account(user_id, connected_account_id)
        
        # Clone payment method to connected account if needed
        connected_pm_id = self.get_or_clone_payment_method(
            platform_payment_method_id, connected_account_id
        )
        
        # Process payment on connected account
        intent = self.client.v1.payment_intents.create(
            params={
                "amount": amount,
                "currency": "usd",
                "customer": linked_customer_id,
                "payment_method": connected_pm_id,
                "confirm": True
            },
            options={"stripe_account": connected_account_id}
        )
        
        return intent
  
  class SetupIntentLinkingModel:
    """Use Setup Intents to link payment methods on demand"""
    
    def create_setup_intent_for_account(self, 
                                      user_id: str, 
                                      connected_account_id: str):
        """Create setup intent on connected account for payment method collection"""
        
        # Get or create linked customer
        linked_customer_id = self.link_to_connected_account(user_id, connected_account_id)
        
        # Create setup intent on connected account
        setup_intent = self.client.v1.setup_intents.create(
            params={
                "customer": linked_customer_id,
                "payment_method_types": ["card"],
                "usage": "off_session",
                "metadata": {
                    "platform_user_id": user_id,
                    "source": "on_demand_linking"
                }
            },
            options={"stripe_account": connected_account_id}
        )
        
        return setup_intent.client_secret
    
    def link_existing_platform_payment_method(self, 
                                            user_id: str, 
                                            connected_account_id: str, 
                                            platform_payment_method_id: str):
        """Link existing platform payment method to connected account"""
        
        # This requires the customer to re-authorize their payment method
        # for the connected account due to Stripe's security requirements
        
        setup_intent_secret = self.create_setup_intent_for_account(
            user_id, connected_account_id
        )
        
        return {
            "requires_reauthorization": True,
            "setup_intent_client_secret": setup_intent_secret,
            "message": "Customer needs to re-authorize payment method for this merchant"
        }

Customer

class LinkedCustomerModel:
    """Platform customer with on-demand connected account linking"""
    
    def create_platform_customer(self, user_id: str, user_data: dict):
        """Create main customer on platform account"""
        
        # Create customer on platform (no stripe_account header)
        platform_customer = self.client.v1.customers.create(
            params={
                "name": user_data.get("name"),
                "email": user_data.get("email"),
                "phone": user_data.get("phone"),
                "metadata": {
                    "platform_user_id": user_id,
                    "is_platform_master": "true"
                }
            }
        )
        
        # Store in database
        self.db.store_platform_customer(user_id, platform_customer.id)
        return platform_customer
    
    def link_to_connected_account(self, user_id: str, connected_account_id: str):
        """Create linked customer on connected account when needed"""
        
        # Get platform customer data
        platform_customer_id = self.db.get_platform_customer_id(user_id)
        platform_customer = self.client.v1.customers.retrieve(platform_customer_id)
        
        # Check if already linked
        existing_link = self.db.get_linked_customer(user_id, connected_account_id)
        if existing_link:
            return existing_link
        
        # Create customer on connected account with same data
        linked_customer = self.client.v1.customers.create(
            params={
                "name": platform_customer.name,
                "email": platform_customer.email, 
                "phone": platform_customer.phone,
                "metadata": {
                    **platform_customer.metadata,
                    "platform_customer_id": platform_customer_id,
                    "linked_from_platform": "true"
                }
            },
            options={"stripe_account": connected_account_id}
        )
        
        # Store the link
        self.db.store_customer_link(
            user_id=user_id,
            platform_customer_id=platform_customer_id,
            connected_account_id=connected_account_id,
            linked_customer_id=linked_customer.id
        )
        
        return linked_customer.id
    
    def sync_customer_data(self, user_id: str, updated_data: dict):
        """Update customer data across all linked accounts"""
        
        platform_customer_id = self.db.get_platform_customer_id(user_id)
        
        # Update platform customer
        self.client.v1.customers.modify(
            platform_customer_id,
            params=updated_data
        )
        
        # Update all linked customers
        linked_customers = self.db.get_all_linked_customers(user_id)
        for link in linked_customers:
            try:
                self.client.v1.customers.modify(
                    link["linked_customer_id"],
                    params=updated_data,
                    options={"stripe_account": link["connected_account_id"]}
                )
            except Exception as e:
                print(f"Failed to sync to {link['connected_account_id']}: {e}")

Charges

def charge_with_manual_capture(self, 
                             customer_id: str, 
                             amount: int, 
                             connected_account_id: str, 
                             delay_days: int,
                             payment_method_id: str):
    """For delays ≤ 7 days: Use manual capture"""
    
    # Create PaymentIntent with manual capture
    intent = self.client.v1.payment_intents.create(
        params={
            "amount": amount,
            "currency": "usd",
            "customer": customer_id,
            "payment_method": payment_method_id,
            "capture_method": "manual",
            "payment_method_options": {
                "card": {
                    "request_extended_authorization": "if_available"
                }
            },
            "metadata": {
                "delay_days": str(delay_days),
                "capture_after": str(int(time.time()) + (delay_days * 24 * 60 * 60)),
                "risk_review": "pending"
            }
        },
        options={
            "stripe_account": connected_account_id,
            **self.req_options
        }
    )
    
    # Confirm the payment (authorize funds)
    confirmed_intent = self.client.v1.payment_intents.confirm(
        intent.id,
        options={
            "stripe_account": connected_account_id,
            **self.req_options
        }
    )
    
    # Schedule capture after delay period
    self.schedule_delayed_capture(
        payment_intent_id=confirmed_intent.id,
        connected_account_id=connected_account_id,
        delay_days=delay_days
    )
    
    return {
        "success": True,
        "payment_intent_id": confirmed_intent.id,
        "status": "authorized",
        "capture_method": "manual",
        "capture_scheduled_for": delay_days,
        "connected_account": connected_account_id
    }

def charge_with_delayed_payout(self, 
                             customer_id: str, 
                             amount: int, 
                             connected_account_id: str, 
                             delay_days: int,
                             payment_method_id: str):
    """For delays > 7 days: Immediate capture + delayed payout"""
    
    # Immediate capture (money collected now)
    intent = self.client.v1.payment_intents.create(
        params={
            "amount": amount,
            "currency": "usd", 
            "customer": customer_id,
            "payment_method": payment_method_id,
            "capture_method": "automatic",  # Capture immediately
            "metadata": {
                "delay_days": str(delay_days),
                "payout_after": str(int(time.time()) + (delay_days * 24 * 60 * 60)),
                "risk_review": "pending",
                "delayed_payout": "true"
            }
        },
        options={
            "stripe_account": connected_account_id,
            **self.req_options
        }
    )
    
    # Confirm payment (money captured immediately)
    confirmed_intent = self.client.v1.payment_intents.confirm(
        intent.id,
        options={
            "stripe_account": connected_account_id,
            **self.req_options
        }
    )
    
    # Hold funds by disabling payouts temporarily (if needed)
    # Or use your own escrow logic
    self.schedule_delayed_payout_release(
        payment_intent_id=confirmed_intent.id,
        connected_account_id=connected_account_id,
        delay_days=delay_days
    )
    
    return {
        "success": True,
        "payment_intent_id": confirmed_intent.id,
        "status": "succeeded",
        "capture_method": "immediate",
        "payout_delayed_for": delay_days,
        "connected_account": connected_account_id
    }

def schedule_delayed_capture(self, payment_intent_id: str, connected_account_id: str, delay_days: int):
    """Schedule capture after risk review period"""
    
    # Store in your database for processing
    capture_time = datetime.now() + timedelta(days=delay_days)
    
    self.db.create_scheduled_task({
        "task_type": "capture_payment",
        "payment_intent_id": payment_intent_id,
        "connected_account_id": connected_account_id,
        "scheduled_for": capture_time,
        "status": "pending"
    })
    
    # Or use your task queue (Celery, Cloud Tasks, etc.)
    # schedule_task("capture_payment", delay=delay_days*24*3600, 
    #              args=[payment_intent_id, connected_account_id])

def schedule_delayed_payout_release(self, payment_intent_id: str, connected_account_id: str, delay_days: int):
    """Schedule payout release after risk review period"""
    
    release_time = datetime.now() + timedelta(days=delay_days)
    
    self.db.create_scheduled_task({
        "task_type": "release_payout",
        "payment_intent_id": payment_intent_id,
        "connected_account_id": connected_account_id,
        "scheduled_for": release_time,
        "status": "pending"
    })

def execute_delayed_capture(self, payment_intent_id: str, connected_account_id: str):
    """Execute scheduled capture after risk review"""
    
    try:
        # Capture the authorized payment
        captured_intent = self.client.v1.payment_intents.capture(
            payment_intent_id,
            options={
                "stripe_account": connected_account_id,
                **self.req_options
            }
        )
        
        return {
            "success": True,
            "payment_intent_id": captured_intent.id,
            "status": "captured",
            "message": "Payment captured after risk review period"
        }
        
    except Exception as e:
        # Handle expired authorization
        if "authorization" in str(e).lower():
            return {
                "success": False,
                "error": "Authorization expired",
                "payment_intent_id": payment_intent_id,
                "requires_new_payment": True
            }
        
        return {"success": False, "error": str(e)}

def cancel_delayed_payment(self, payment_intent_id: str, connected_account_id: str, reason: str = "fraud_detected"):
    """Cancel payment if fraud detected during review period"""
    
    try:
        # Cancel the payment intent
        cancelled_intent = self.client.v1.payment_intents.cancel(
            payment_intent_id,
            params={"cancellation_reason": reason},
            options={
                "stripe_account": connected_account_id,
                **self.req_options
            }
        )
        
        # Update scheduled task
        self.db.update_scheduled_task(payment_intent_id, {"status": "cancelled"})
        
        return {
            "success": True,
            "payment_intent_id": cancelled_intent.id,
            "status": "cancelled",
            "reason": reason
        }
        
    except Exception as e:
        return {"success": False, "error": str(e)}
    


def charge_with_manual_capture(self, 
                             customer_id: str, 
                             amount: int, 
                             connected_account_id: str, 
                             delay_days: int,
                             payment_method_id: str):
    """For delays ≤ 7 days: Authorize now, capture later"""
    
    # Create PaymentIntent with manual capture
    intent = self.client.v1.payment_intents.create(
        params={
            "amount": amount,
            "currency": "usd",
            "customer": customer_id,
            "payment_method": payment_method_id,
            "capture_method": "manual",  # Don't capture automatically
            "metadata": {
                "delay_days": str(delay_days),
                "capture_after": str(int(time.time()) + (delay_days * 24 * 60 * 60))
            }
        },
        options={"stripe_account": connected_account_id}
    )
    
    # Confirm = AUTHORIZE (hold funds, don't capture yet)
    confirmed_intent = self.client.v1.payment_intents.confirm(
        intent.id,
        options={"stripe_account": connected_account_id}
    )
    # Status: requires_capture
    
    # Schedule capture for later (after delay period)
    self.schedule_delayed_capture(
        payment_intent_id=confirmed_intent.id,
        connected_account_id=connected_account_id,
        delay_days=delay_days
    )
    
    return {
        "success": True,
        "payment_intent_id": confirmed_intent.id,
        "status": confirmed_intent.status,  # "requires_capture"
        "authorization_expires_in_days": 7,  # Or up to 31 with extended auth
        "capture_scheduled_in_days": delay_days
    }

def execute_delayed_capture(self, payment_intent_id: str, connected_account_id: str):
    """Actually capture the authorized payment after delay period"""
    
    try:
        # NOW capture the funds (this actually charges the customer)
        captured_intent = self.client.v1.payment_intents.capture(
            payment_intent_id,
            options={"stripe_account": connected_account_id}
        )
        # Status changes from "requires_capture" to "succeeded"
        
        return {
            "success": True,
            "payment_intent_id": captured_intent.id,
            "status": captured_intent.status,  # "succeeded" 
            "amount_captured": captured_intent.amount_captured
        }
        
    except Exception as e:
        return {"success": False, "error": str(e)}
    


def charge_platform_with_delayed_transfer(self, 
                                        customer_id: str, 
                                        amount: int, 
                                        connected_account_id: str, 
                                        delay_days: int,
                                        payment_method_id: str):
    """Charge platform account immediately, transfer to connected account later"""
    
    # Create customer on PLATFORM account (not connected account)
    platform_customer_id = self.get_or_create_platform_customer(customer_id)
    
    # Charge platform account immediately
    platform_intent = self.client.v1.payment_intents.create(
        params={
            "amount": amount,
            "currency": "usd",
            "customer": platform_customer_id,  # Platform customer
            "payment_method": payment_method_id,
            "capture_method": "automatic",     # Capture immediately
            "metadata": {
                "connected_account_id": connected_account_id,
                "transfer_after": str(int(time.time()) + (delay_days * 24 * 60 * 60)),
                "transfer_amount": str(amount - platform_fee)
            }
        }
        # No stripe_account header = platform charge
    )
    
    confirmed_intent = self.client.v1.payment_intents.confirm(platform_intent.id)
    
    # Schedule transfer to connected account after delay
    self.schedule_delayed_transfer_to_connected_account(
        platform_payment_intent_id=confirmed_intent.id,
        connected_account_id=connected_account_id,
        transfer_amount=amount - platform_fee,
        delay_days=delay_days
    )
    
    return {
        "success": True,
        "platform_payment_intent_id": confirmed_intent.id,
        "status": "captured_on_platform",
        "transfer_scheduled_in_days": delay_days,
        "method": "platform_escrow"
    }

def execute_delayed_transfer(self, 
                           connected_account_id: str, 
                           amount: int):
    """Transfer funds from platform to connected account after delay"""
    
    transfer = self.client.v1.transfers.create(
        params={
            "amount": amount,
            "currency": "usd",
            "destination": connected_account_id,
            "metadata": {"delayed_transfer": "true"}
        }
    )
    
    return transfer