TODO
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 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}")
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"
}
{ “id”: “link_123”, “user_id”: “user_456”, “platform_customer_id”: “cus_platform_789”, “connected_account_id”: “acct_connected_012”, “linked_customer_id”: “cus_linked_345”, “created_at”: “2024-01-01T00:00:00Z”, “status”: “active” }
{ “id”: “pm_link_123”, “user_id”: “user_456”, “platform_payment_method_id”: “pm_platform_789”, “connected_account_id”: “acct_connected_012”, “connected_payment_method_id”: “pm_connected_345”, “created_at”: “2024-01-01T00:00:00Z” } class PlatformCustomerModel: “““Simple: Platform owns customers, charges connected accounts directly”””
def create_customer(self, user_id: str, user_data: dict):
"""One customer on platform account"""
customer = self.client.v1.customers.create(
params={
"name": user_data.get("name"),
"email": user_data.get("email"),
"metadata": {"platform_user_id": user_id}
}
# No stripe_account header = platform customer
)
return customer
def charge_any_connected_account(self,
customer_id: str,
connected_account_id: str,
amount: int,
payment_method_id: str):
"""Charge any connected account using platform customer"""
intent = self.client.v1.payment_intents.create(
params={
"amount": amount,
"currency": "usd",
"customer": customer_id, # Platform customer
"payment_method": payment_method_id, # Platform payment method
"on_behalf_of": connected_account_id, # Money goes to connected account
"confirm": True
}
)
return intent
class ConnectedAccountModel: “““Simple: Each connected account has its own customers”””
def get_or_create_customer_for_account(self,
user_id: str,
connected_account_id: str,
user_data: dict):
"""Get/create customer specific to connected account"""
# Check database for existing customer on this account
existing = self.db.get_customer_for_account(user_id, connected_account_id)
if existing:
return existing
# Create new customer on connected account
customer = self.client.v1.customers.create(
params={
"name": user_data.get("name"),
"email": user_data.get("email"),
"metadata": {"platform_user_id": user_id}
},
options={"stripe_account": connected_account_id}
)
# Store mapping
self.db.store_account_customer(user_id, connected_account_id, customer.id)
return customer.id
def charge_connected_account(self,
user_id: str,
connected_account_id: str,
amount: int,
payment_method_id: str):
"""Charge using account-specific customer"""
customer_id = self.get_or_create_customer_for_account(
user_id, connected_account_id, user_data
)
intent = self.client.v1.payment_intents.create(
params={
"amount": amount,
"currency": "usd",
"customer": customer_id, # Account-specific customer
"payment_method": payment_method_id,
"confirm": True
},
options={"stripe_account": connected_account_id}
)
return intent
class MetadataSearchModel: “““Use Stripe’s search to find customers by metadata”””
def find_customer_by_platform_user_id(self, user_id: str, connected_account_id: str):
"""Search for customer using platform user ID"""
try:
# Search customers by metadata (requires newer Stripe API)
customers = self.client.v1.customers.search(
params={
"query": f"metadata['platform_user_id']:'{user_id}'",
"limit": 1
},
options={"stripe_account": connected_account_id}
)
if customers.data:
return customers.data[0].id
except Exception as e:
# Fallback to list with email
print(f"Search failed, using fallback: {e}")
return self.fallback_find_by_email(user_id, connected_account_id)
return None
def get_or_create_customer(self, user_id: str, connected_account_id: str):
"""Find by search or create new"""
customer_id = self.find_customer_by_platform_user_id(user_id, connected_account_id)
if customer_id:
return customer_id
# Create new customer with searchable metadata
user_data = self.get_user_data(user_id)
customer = self.client.v1.customers.create(
params={
"email": user_data["email"],
"name": user_data["name"],
"metadata": {
"platform_user_id": user_id, # Searchable
"created_by_platform": "true"
}
},
options={"stripe_account": connected_account_id}
)
return customer.id
class MinimalMappingModel: “““Store only essential mappings”””
def get_or_create_customer(self, user_id: str, connected_account_id: str):
"""Check DB first, then create if needed"""
# Simple table: user_id | connected_account_id | stripe_customer_id
existing = self.db.execute("""
SELECT stripe_customer_id
FROM customer_mappings
WHERE user_id = ? AND connected_account_id = ?
""", (user_id, connected_account_id)).fetchone()
if existing:
return existing[0]
# Create new customer
user_data = self.get_user_data(user_id)
customer = self.client.v1.customers.create(
params={
"email": user_data["email"],
"name": user_data["name"],
"metadata": {"platform_user_id": user_id}
},
options={"stripe_account": connected_account_id}
)
# Store minimal mapping
self.db.execute("""
INSERT INTO customer_mappings (user_id, connected_account_id, stripe_customer_id)
VALUES (?, ?, ?)
""", (user_id, connected_account_id, customer.id))
return customer.id
def charge_with_delayed_transfer(self, app_user_id: str, amount: int, connected_account_id: str, delay_days: int, payment_method_id: str, user_data: dict): “““Charge connected account customer with delayed transfer for fraud prevention”””
try:
# Get or create customer on connected account
customer_id = self.get_or_create_connected_customer(
app_user_id, connected_account_id, user_data
)
if delay_days <= 7:
# Use manual capture for delays up to 7 days
return self.charge_with_manual_capture(
customer_id, amount, connected_account_id,
delay_days, payment_method_id
)
else:
# Use immediate capture + delayed payout for longer delays
return self.charge_with_delayed_payout(
customer_id, amount, connected_account_id,
delay_days, payment_method_id
)
except Exception as e:
return {"error": str(e), "success": False}
def get_or_create_connected_customer(self, app_user_id: str, connected_account_id: str, user_data: dict): “““Get or create customer directly on connected account”””
# Search for existing customer by app user ID
customers = self.client.v1.customers.list(
params={"limit": 100},
options={"stripe_account": connected_account_id}
)
for customer in customers.data:
if customer.metadata.get("app_user_id") == app_user_id:
return customer.id
# Create new customer on connected account
customer = self.client.v1.customers.create(
params={
"name": user_data.get("name"),
"email": user_data.get("email"),
"metadata": {
"app_user_id": app_user_id,
"created_by_platform": "true"
}
},
options={
"stripe_account": connected_account_id,
**self.req_options
}
)
return customer.id
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