> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dodopayments.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Upsells & Downsells

> बचाए गए भुगतान तरीकों का उपयोग करके पोस्ट-पर्चेस ऑफ़र, सब्सक्रिप्शन अपग्रेड और क्रॉस-सेल्स के लिए एक-क्लिक खरीदारी के साथ राजस्व बढ़ाएँ।

<Info>
  Upsells और downsells आपको संयोजित भुगतान तरीके का उपयोग करके ग्राहकों को अतिरिक्त उत्पाद या योजना परिवर्तन प्रदान करने देता है। इससे एक-क्लिक खरीदारी संभव होती है जो भुगतान वसूली को छोड़ देती है, और रूपांतरण दरों में जबरदस्त सुधार होता है।
</Info>

<CardGroup cols={3}>
  <Card title="Post-Purchase Upsells" icon="cart-plus">
    चेकआउट के तुरंत बाद पूरक उत्पाद एक-क्लिक खरीदारी के साथ पेश करें।
  </Card>

  <Card title="Subscription Upgrades" icon="arrow-up">
    स्वचालित प्रोरैशन और तात्कालिक बिलिंग के साथ ग्राहकों को उच्चतर स्तर में स्थानांतरित करें।
  </Card>

  <Card title="Cross-Sells" icon="grid-2-plus">
    मौजूदा ग्राहकों को संबंधित उत्पाद जोड़ें बिना भुगतान विवरण फिर से दर्ज किए।
  </Card>
</CardGroup>

## अवलोकन

Upsells और downsells शक्तिशाली राजस्व अनुकूलन रणनीतियाँ हैं:

* **Upsells**: उच्च-मूल्य उत्पाद या अपग्रेड (उदा., Basic के बजाय Pro योजना) पेश करें
* **Downsells**: जब ग्राहक अस्वीकार या डाउनग्रेड करे तो कम कीमत वाला विकल्प पेश करें
* **Cross-sells**: पूरक उत्पाद सुझाएँ (उदा., ऐड-ऑन, संबंधित आइटम)

Dodo Payments `payment_method_id` पैरामीटर के माध्यम से इन फ्लो को सक्षम करता है, जो आपको ग्राहक का भुगतान तरीका बिना कार्ड विवरण दोबारा दर्ज किए चार्ज करने देता है।

### मुख्य लाभ

| लाभ                      | प्रभाव                                              |
| ------------------------ | --------------------------------------------------- |
| **एक-क्लिक खरीदारी**     | लौटते ग्राहकों के लिए भुगतान फॉर्म पूरी तरह छोड़ें  |
| **उच्च रूपांतरण**        | निर्णय के समय घर्षण कम करें                         |
| **तात्कालिक प्रोसेसिंग** | `confirm: true` के साथ चार्ज तुरंत संसाधित होते हैं |
| **सतत UX**               | ग्राहक पूरे फ्लो के दौरान आपके ऐप में बने रहते हैं  |

## यह कैसे काम करता है

```mermaid theme={null}
sequenceDiagram
    participant C as Customer
    participant A as Your App
    participant D as Dodo API
    
    C->>A: Completes purchase
    A->>D: Get payment methods
    D-->>A: payment_method_id
    A->>C: Show upsell offer
    C->>A: Accept (one click)
    A->>D: Charge with payment_method_id
    D-->>A: Payment success
    D->>A: Webhook: payment.succeeded
```

## पूर्वापेक्षाएँ

Upsells और downsells लागू करने से पहले सुनिश्चित करें कि आपके पास है:

<Steps>
  <Step title="Customer with Saved Payment Method">
    ग्राहकों ने कम से कम एक खरीद पूरी की होनी चाहिए। भुगतान तरीके स्वचालित रूप से सहेजे जाते हैं जब ग्राहक चेकआउट पूरी करते हैं।
  </Step>

  <Step title="Products Configured">
    Dodo Payments डैशबोर्ड में अपने upsell उत्पाद बनाएँ। ये एक बार के भुगतान, सदस्यता या ऐड-ऑन हो सकते हैं।
  </Step>

  <Step title="Webhook Endpoint">
    `payment.succeeded`, `payment.failed`, और `subscription.plan_changed` इवेंट्स को संभालने के लिए वेबहुक सेटअप करें।
  </Step>
</Steps>

## ग्राहक भुगतान तरीके प्राप्त करना

Upsell पेश करने से पहले ग्राहक के सहेजे गए भुगतान तरीके प्राप्त करें:

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    import DodoPayments from 'dodopayments';

    const client = new DodoPayments({
      bearerToken: process.env.DODO_PAYMENTS_API_KEY,
      environment: 'live_mode',
    });

    async function getPaymentMethods(customerId: string) {
      const paymentMethods = await client.customers.retrievePaymentMethods(customerId);
      
      // Returns array of saved payment methods
      // Each has: payment_method_id, type, card (last4, brand, exp_month, exp_year)
      return paymentMethods;
    }

    // Example usage
    const methods = await getPaymentMethods('cus_123');
    console.log('Available payment methods:', methods);

    // Use the first available method for upsell
    const primaryMethod = methods[0]?.payment_method_id;
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    import os
    from dodopayments import DodoPayments

    client = DodoPayments(
        bearer_token=os.environ.get("DODO_PAYMENTS_API_KEY"),
        environment="live_mode",
    )

    def get_payment_methods(customer_id: str):
        payment_methods = client.customers.retrieve_payment_methods(customer_id)
        
        # Returns list of saved payment methods
        # Each has: payment_method_id, type, card (last4, brand, exp_month, exp_year)
        return payment_methods

    # Example usage
    methods = get_payment_methods("cus_123")
    print("Available payment methods:", methods)

    # Use the first available method for upsell
    primary_method = methods[0].payment_method_id if methods else None
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    package main

    import (
        "context"
        "fmt"
        "github.com/dodopayments/dodopayments-go"
        "github.com/dodopayments/dodopayments-go/option"
    )

    func getPaymentMethods(customerID string) ([]dodopayments.CustomerGetPaymentMethodsResponseItem, error) {
        client := dodopayments.NewClient(
            option.WithBearerToken(os.Getenv("DODO_PAYMENTS_API_KEY")),
        )
        
        resp, err := client.Customers.GetPaymentMethods(
            context.TODO(),
            customerID,
        )
        if err != nil {
            return nil, err
        }
        
        return resp.Items, nil
    }

    func main() {
        methods, err := getPaymentMethods("cus_123")
        if err != nil {
            panic(err)
        }
        
        fmt.Println("Available payment methods:", methods)
        
        // Use the first available method for upsell
        if len(methods) > 0 {
            primaryMethod := methods[0].PaymentMethodID
            fmt.Println("Primary method:", primaryMethod)
        }
    }
    ```
  </Tab>
</Tabs>

<Info>
  ग्राहक चेकआउट पूरी करते ही भुगतान तरीके स्वचालित रूप से सहेजे जाते हैं। आपको उन्हें स्पष्ट रूप से सहेजने की आवश्यकता नहीं है।
</Info>

## पोस्ट-खरीद एक-क्लिक Upsells

सफल खरीद के तुरंत बाद अतिरिक्त उत्पाद पेश करें। ग्राहक एक ही क्लिक में स्वीकार कर सकते हैं क्योंकि उनका भुगतान तरीका पहले से सहेजा गया है।

```mermaid theme={null}
flowchart LR
    A[Purchase] --> B{Upsell?}
    B -->|Yes| C[Show Offer]
    B -->|No| F[Done]
    C --> D{Accept?}
    D -->|Yes| E[One-Click Charge] --> F
    D -->|No| F
```

### कार्यान्वयन

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    import DodoPayments from 'dodopayments';

    const client = new DodoPayments({
      bearerToken: process.env.DODO_PAYMENTS_API_KEY,
      environment: 'live_mode',
    });

    async function createOneClickUpsell(
      customerId: string,
      paymentMethodId: string,
      upsellProductId: string
    ) {
      // Create checkout session with saved payment method
      // confirm: true processes the payment immediately
      const session = await client.checkoutSessions.create({
        product_cart: [
          {
            product_id: upsellProductId,
            quantity: 1
          }
        ],
        customer: {
          customer_id: customerId
        },
        payment_method_id: paymentMethodId,
        confirm: true,  // Required when using payment_method_id
        return_url: 'https://yourapp.com/upsell-success',
        feature_flags: {
          redirect_immediately: true  // Skip success page
        },
        metadata: {
          upsell_source: 'post_purchase',
          original_order_id: 'order_123'
        }
      });

      return session;
    }

    // Example: Offer premium add-on after initial purchase
    async function handlePostPurchaseUpsell(customerId: string) {
      // Get customer's payment methods
      const methods = await client.customers.retrievePaymentMethods(customerId);
      
      if (methods.length === 0) {
        console.log('No saved payment methods available');
        return null;
      }

      // Create the upsell with one-click checkout
      const upsell = await createOneClickUpsell(
        customerId,
        methods[0].payment_method_id,
        'prod_premium_addon'
      );

      console.log('Upsell processed:', upsell.session_id);
      return upsell;
    }
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    import os
    from dodopayments import DodoPayments

    client = DodoPayments(
        bearer_token=os.environ.get("DODO_PAYMENTS_API_KEY"),
        environment="live_mode",
    )

    def create_one_click_upsell(
        customer_id: str,
        payment_method_id: str,
        upsell_product_id: str
    ):
        """Create a one-click upsell using saved payment method."""
        
        # Create checkout session with saved payment method
        # confirm=True processes the payment immediately
        session = client.checkout_sessions.create(
            product_cart=[
                {
                    "product_id": upsell_product_id,
                    "quantity": 1
                }
            ],
            customer={
                "customer_id": customer_id
            },
            payment_method_id=payment_method_id,
            confirm=True,  # Required when using payment_method_id
            return_url="https://yourapp.com/upsell-success",
            feature_flags={
                "redirect_immediately": True  # Skip success page
            },
            metadata={
                "upsell_source": "post_purchase",
                "original_order_id": "order_123"
            }
        )
        
        return session

    def handle_post_purchase_upsell(customer_id: str):
        """Offer premium add-on after initial purchase."""
        
        # Get customer's payment methods
        methods = client.customers.retrieve_payment_methods(customer_id)
        
        if not methods:
            print("No saved payment methods available")
            return None
        
        # Create the upsell with one-click checkout
        upsell = create_one_click_upsell(
            customer_id=customer_id,
            payment_method_id=methods[0].payment_method_id,
            upsell_product_id="prod_premium_addon"
        )
        
        print(f"Upsell processed: {upsell.session_id}")
        return upsell
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    package main

    import (
        "context"
        "fmt"
        "os"
        
        "github.com/dodopayments/dodopayments-go"
        "github.com/dodopayments/dodopayments-go/option"
    )

    func createOneClickUpsell(
        customerID string,
        paymentMethodID string,
        upsellProductID string,
    ) (*dodopayments.CheckoutSessionResponse, error) {
        client := dodopayments.NewClient(
            option.WithBearerToken(os.Getenv("DODO_PAYMENTS_API_KEY")),
        )
        
        // Create checkout session with saved payment method
        // Confirm: true processes the payment immediately
        session, err := client.CheckoutSessions.New(context.TODO(), dodopayments.CheckoutSessionNewParams{
            CheckoutSessionRequest: dodopayments.CheckoutSessionRequestParam{
                ProductCart: dodopayments.F([]dodopayments.ProductItemReqParam{
                    {
                        ProductID: dodopayments.F(upsellProductID),
                        Quantity:  dodopayments.F(int64(1)),
                    },
                }),
                Customer: dodopayments.F[dodopayments.CustomerRequestUnionParam](
                    dodopayments.AttachExistingCustomerParam{
                        CustomerID: dodopayments.F(customerID),
                    },
                ),
                PaymentMethodID: dodopayments.F(paymentMethodID),
                Confirm:         dodopayments.F(true), // Required when using payment_method_id
                ReturnURL:       dodopayments.F("https://yourapp.com/upsell-success"),
                FeatureFlags: dodopayments.F(dodopayments.CheckoutSessionFlagsParam{
                    RedirectImmediately: dodopayments.F(true), // Skip success page
                }),
                Metadata: dodopayments.F(map[string]string{
                    "upsell_source":     "post_purchase",
                    "original_order_id": "order_123",
                }),
            },
        })
        
        return session, err
    }

    func handlePostPurchaseUpsell(customerID string) (*dodopayments.CheckoutSessionResponse, error) {
        client := dodopayments.NewClient(
            option.WithBearerToken(os.Getenv("DODO_PAYMENTS_API_KEY")),
        )
        
        // Get customer's payment methods
        resp, err := client.Customers.GetPaymentMethods(context.TODO(), customerID)
        if err != nil {
            return nil, err
        }
        
        if len(resp.Items) == 0 {
            fmt.Println("No saved payment methods available")
            return nil, nil
        }
        
        // Create the upsell with one-click checkout
        upsell, err := createOneClickUpsell(
            customerID,
            resp.Items[0].PaymentMethodID,
            "prod_premium_addon",
        )
        if err != nil {
            return nil, err
        }
        
        fmt.Printf("Upsell processed: %s\n", upsell.SessionID)
        return upsell, nil
    }
    ```
  </Tab>
</Tabs>

<Warning>
  जब `payment_method_id` का उपयोग किया जाता है, तो `confirm: true` सेट करना और एक मौजूदा `customer_id` प्रदान करना आवश्यक है। भुगतान तरीका उसी ग्राहक का होना चाहिए।
</Warning>

## सदस्यता अपग्रेड्स

स्वचालित प्रोरैशन हैंडलिंग के साथ ग्राहकों को उच्च-स्तरीय सदस्यता योजनाओं में स्थानांतरित करें।

```mermaid theme={null}
sequenceDiagram
    participant C as Customer
    participant A as Your App
    participant D as Dodo API
    
    C->>A: Request upgrade
    A->>D: Preview change plan
    D-->>A: Charge amount
    A->>C: Confirm $50 charge?
    C->>A: Confirm
    A->>D: Change plan
    D->>A: Webhook: plan_changed
```

### प्रतिबद्धता से पहले पूर्वावलोकन

हमेशा योजना परिवर्तनों का पूर्वावलोकन करें ताकि ग्राहकों को ठीक से पता चले कि उन्हें कितना चार्ज किया जाएगा:

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    async function previewUpgrade(
      subscriptionId: string,
      newProductId: string
    ) {
      const preview = await client.subscriptions.previewChangePlan(subscriptionId, {
        product_id: newProductId,
        quantity: 1,
        proration_billing_mode: 'difference_immediately'
      });

      return {
        immediateCharge: preview.immediate_charge?.summary,
        newPlan: preview.new_plan,
        effectiveDate: preview.effective_date
      };
    }

    // Show customer the charge before confirming
    const preview = await previewUpgrade('sub_123', 'prod_pro_plan');
    console.log(`Upgrade will charge: ${preview.immediateCharge}`);
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    def preview_upgrade(subscription_id: str, new_product_id: str):
        preview = client.subscriptions.preview_change_plan(
            subscription_id=subscription_id,
            product_id=new_product_id,
            quantity=1,
            proration_billing_mode="difference_immediately"
        )
        
        return {
            "immediate_charge": preview.immediate_charge.summary if preview.immediate_charge else None,
            "new_plan": preview.new_plan,
            "effective_date": preview.effective_date
        }

    # Show customer the charge before confirming
    preview = preview_upgrade("sub_123", "prod_pro_plan")
    print(f"Upgrade will charge: {preview['immediate_charge']}")
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    func previewUpgrade(subscriptionID string, newProductID string) (map[string]interface{}, error) {
        client := dodopayments.NewClient(
            option.WithBearerToken(os.Getenv("DODO_PAYMENTS_API_KEY")),
        )
        
        preview, err := client.Subscriptions.PreviewChangePlan(
            context.TODO(),
            subscriptionID,
            dodopayments.SubscriptionPreviewChangePlanParams{
                UpdateSubscriptionPlanReq: dodopayments.UpdateSubscriptionPlanReqParam{
                    ProductID:            dodopayments.F(newProductID),
                    Quantity:             dodopayments.F(int64(1)),
                    ProrationBillingMode: dodopayments.F(dodopayments.UpdateSubscriptionPlanReqProrationBillingModeDifferenceImmediately),
                },
            },
        )
        if err != nil {
            return nil, err
        }
        
        return map[string]interface{}{
            "immediate_charge": preview.ImmediateCharge.Summary,
            "new_plan":         preview.NewPlan,
            "effective_at":     preview.ImmediateCharge.EffectiveAt,
        }, nil
    }
    ```
  </Tab>
</Tabs>

### अपग्रेड को निष्पादित करें

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    async function upgradeSubscription(
      subscriptionId: string,
      newProductId: string,
      prorationMode: 'prorated_immediately' | 'difference_immediately' | 'full_immediately' = 'difference_immediately'
    ) {
      const result = await client.subscriptions.changePlan(subscriptionId, {
        product_id: newProductId,
        quantity: 1,
        proration_billing_mode: prorationMode
      });

      return {
        status: result.status,
        subscriptionId: result.subscription_id,
        paymentId: result.payment_id,
        invoiceId: result.invoice_id
      };
    }

    // Upgrade from Basic ($30) to Pro ($80)
    // With difference_immediately: charges $50 instantly
    const upgrade = await upgradeSubscription('sub_123', 'prod_pro_plan');
    console.log('Upgrade status:', upgrade.status);
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    def upgrade_subscription(
        subscription_id: str,
        new_product_id: str,
        proration_mode: str = "difference_immediately"
    ):
        result = client.subscriptions.change_plan(
            subscription_id=subscription_id,
            product_id=new_product_id,
            quantity=1,
            proration_billing_mode=proration_mode
        )
        
        return {
            "status": result.status,
            "subscription_id": result.subscription_id,
            "payment_id": result.payment_id,
            "invoice_id": result.invoice_id
        }

    # Upgrade from Basic ($30) to Pro ($80)
    # With difference_immediately: charges $50 instantly
    upgrade = upgrade_subscription("sub_123", "prod_pro_plan")
    print(f"Upgrade status: {upgrade['status']}")
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    func upgradeSubscription(
        subscriptionID string,
        newProductID string,
        prorationMode dodopayments.UpdateSubscriptionPlanReqProrationBillingMode,
    ) error {
        client := dodopayments.NewClient(
            option.WithBearerToken(os.Getenv("DODO_PAYMENTS_API_KEY")),
        )
        
        // ChangePlan returns no response body; a nil error means the change succeeded.
        err := client.Subscriptions.ChangePlan(
            context.TODO(),
            subscriptionID,
            dodopayments.SubscriptionChangePlanParams{
                UpdateSubscriptionPlanReq: dodopayments.UpdateSubscriptionPlanReqParam{
                    ProductID:            dodopayments.F(newProductID),
                    Quantity:             dodopayments.F(int64(1)),
                    ProrationBillingMode: dodopayments.F(prorationMode),
                },
            },
        )
        
        return err
    }

    // Upgrade from Basic ($30) to Pro ($80)
    // With DifferenceImmediately: charges $50 instantly
    err := upgradeSubscription(
        "sub_123",
        "prod_pro_plan",
        dodopayments.UpdateSubscriptionPlanReqProrationBillingModeDifferenceImmediately,
    )
    if err != nil {
        panic(err)
    }
    fmt.Println("Upgrade succeeded")
    ```
  </Tab>
</Tabs>

### प्रोरैशन मोड

अपग्रेड करते समय ग्राहकों को कैसे बिल किया जाएगा चुनें:

| मोड                      | व्यवहार                                                    | सर्वश्रेष्ठ के लिए |
| ------------------------ | ---------------------------------------------------------- | ------------------ |
| `difference_immediately` | कीमत अंतर तुरंत चार्ज करता है ($30→$80 = \$50)             | सीधे अपग्रेड       |
| `prorated_immediately`   | बिलिंग चक्र में बचे समय के आधार पर चार्ज करता है           | समय-आधारित बिलिंग  |
| `full_immediately`       | नई योजना की पूरी कीमत चार्ज करता है, बचे समय की परवाह नहीं | बिलिंग चक्र रीसेट  |

<Tip>
  सरल अपग्रेड फ्लो के लिए `difference_immediately` का उपयोग करें। जब आप वर्तमान योजना पर बाकी बचे समय को ध्यान में रखना चाहते हैं तो `prorated_immediately` का उपयोग करें।
</Tip>

## क्रॉस-सेल्स

मौजूदा ग्राहकों के लिए पूरक उत्पाद जोड़ें बिना उन्हें भुगतान विवरण फिर से दर्ज कराने के।

```mermaid theme={null}
flowchart LR
    A[View Product] --> B{Existing Customer?}
    B -->|Yes| C[One-Click Buy] --> E[Deliver]
    B -->|No| D[Standard Checkout] --> E
```

### कार्यान्वयन

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    async function createCrossSell(
      customerId: string,
      paymentMethodId: string,
      productId: string,
      quantity: number = 1
    ) {
      // Create a one-time payment using saved payment method
      const payment = await client.payments.create({
        product_cart: [
          {
            product_id: productId,
            quantity: quantity
          }
        ],
        customer_id: customerId,
        payment_method_id: paymentMethodId,
        return_url: 'https://yourapp.com/purchase-complete',
        metadata: {
          purchase_type: 'cross_sell',
          source: 'product_recommendation'
        }
      });

      return payment;
    }

    // Example: Customer bought a course, offer related ebook
    async function offerRelatedProduct(customerId: string, relatedProductId: string) {
      const methods = await client.customers.retrievePaymentMethods(customerId);
      
      if (methods.length === 0) {
        // Fall back to standard checkout
        return client.checkoutSessions.create({
          product_cart: [{ product_id: relatedProductId, quantity: 1 }],
          customer: { customer_id: customerId },
          return_url: 'https://yourapp.com/purchase-complete'
        });
      }

      // One-click purchase
      return createCrossSell(customerId, methods[0].payment_method_id, relatedProductId);
    }
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    def create_cross_sell(
        customer_id: str,
        payment_method_id: str,
        product_id: str,
        quantity: int = 1
    ):
        """Create a one-time payment using saved payment method."""
        
        payment = client.payments.create(
            product_cart=[
                {
                    "product_id": product_id,
                    "quantity": quantity
                }
            ],
            customer_id=customer_id,
            payment_method_id=payment_method_id,
            return_url="https://yourapp.com/purchase-complete",
            metadata={
                "purchase_type": "cross_sell",
                "source": "product_recommendation"
            }
        )
        
        return payment

    def offer_related_product(customer_id: str, related_product_id: str):
        """Offer related product with one-click purchase if possible."""
        
        methods = client.customers.retrieve_payment_methods(customer_id)
        
        if not methods:
            # Fall back to standard checkout
            return client.checkout_sessions.create(
                product_cart=[{"product_id": related_product_id, "quantity": 1}],
                customer={"customer_id": customer_id},
                return_url="https://yourapp.com/purchase-complete"
            )
        
        # One-click purchase
        return create_cross_sell(customer_id, methods[0].payment_method_id, related_product_id)
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    func createCrossSell(
        customerID string,
        paymentMethodID string,
        productID string,
        quantity int64,
    ) (*dodopayments.PaymentNewResponse, error) {
        client := dodopayments.NewClient(
            option.WithBearerToken(os.Getenv("DODO_PAYMENTS_API_KEY")),
        )
        
        payment, err := client.Payments.New(context.TODO(), dodopayments.PaymentNewParams{
            ProductCart: dodopayments.F([]dodopayments.OneTimeProductCartItemParam{
                {
                    ProductID: dodopayments.F(productID),
                    Quantity:  dodopayments.F(quantity),
                },
            }),
            Customer: dodopayments.F[dodopayments.CustomerRequestUnionParam](
                dodopayments.AttachExistingCustomerParam{CustomerID: dodopayments.F(customerID)},
            ),
            Billing: dodopayments.F(dodopayments.BillingAddressParam{
                Country: dodopayments.F(dodopayments.CountryCodeUs),
                City:    dodopayments.F("San Francisco"),
                State:   dodopayments.F("CA"),
                Street:  dodopayments.F("1 Market St"),
                Zipcode: dodopayments.F("94105"),
            }),
            PaymentMethodID: dodopayments.F(paymentMethodID),
            ReturnURL:       dodopayments.F("https://yourapp.com/purchase-complete"),
            Metadata: dodopayments.F(map[string]string{
                "purchase_type": "cross_sell",
                "source":        "product_recommendation",
            }),
        })
        
        return payment, err
    }

    func offerRelatedProduct(customerID string, relatedProductID string) (interface{}, error) {
        client := dodopayments.NewClient(
            option.WithBearerToken(os.Getenv("DODO_PAYMENTS_API_KEY")),
        )
        
        resp, err := client.Customers.GetPaymentMethods(context.TODO(), customerID)
        if err != nil {
            return nil, err
        }
        
        if len(resp.Items) == 0 {
            // Fall back to standard checkout
            return client.CheckoutSessions.New(context.TODO(), dodopayments.CheckoutSessionNewParams{
                CheckoutSessionRequest: dodopayments.CheckoutSessionRequestParam{
                    ProductCart: dodopayments.F([]dodopayments.ProductItemReqParam{
                        {ProductID: dodopayments.F(relatedProductID), Quantity: dodopayments.F(int64(1))},
                    }),
                    Customer: dodopayments.F[dodopayments.CustomerRequestUnionParam](
                        dodopayments.AttachExistingCustomerParam{CustomerID: dodopayments.F(customerID)},
                    ),
                    ReturnURL: dodopayments.F("https://yourapp.com/purchase-complete"),
                },
            })
        }
        
        // One-click purchase
        return createCrossSell(customerID, resp.Items[0].PaymentMethodID, relatedProductID, 1)
    }
    ```
  </Tab>
</Tabs>

## सदस्यता डाउनग्रेड्स

जब ग्राहक निचले स्तर की योजना पर जाना चाहें, तो स्वचालित क्रेडिट के साथ बदलाव को सहजता से संभालें।

### डाउनग्रेड कैसे काम करते हैं

1. ग्राहक डाउनग्रेड का अनुरोध करता है (Pro → Basic)
2. सिस्टम वर्तमान योजना पर शेष मूल्य की गणना करता है
3. भविष्य के नवीनीकरण के लिए सदस्यता में क्रेडिट जोड़ा जाता है
4. ग्राहक तुरंत नई योजना पर चला जाता है

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    async function downgradeSubscription(
      subscriptionId: string,
      newProductId: string
    ) {
      // Preview the downgrade first
      const preview = await client.subscriptions.previewChangePlan(subscriptionId, {
        product_id: newProductId,
        quantity: 1,
        proration_billing_mode: 'difference_immediately'
      });

      console.log('Credit to be applied:', preview.credit_amount);

      // Execute the downgrade
      const result = await client.subscriptions.changePlan(subscriptionId, {
        product_id: newProductId,
        quantity: 1,
        proration_billing_mode: 'difference_immediately'
      });

      // Credits are automatically applied to future renewals
      return result;
    }

    // Downgrade from Pro ($80) to Basic ($30)
    // $50 credit added to subscription, auto-applied on next renewal
    const downgrade = await downgradeSubscription('sub_123', 'prod_basic_plan');
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    def downgrade_subscription(subscription_id: str, new_product_id: str):
        # Preview the downgrade first
        preview = client.subscriptions.preview_change_plan(
            subscription_id=subscription_id,
            product_id=new_product_id,
            quantity=1,
            proration_billing_mode="difference_immediately"
        )
        
        print(f"Credit to be applied: {preview.credit_amount}")
        
        # Execute the downgrade
        result = client.subscriptions.change_plan(
            subscription_id=subscription_id,
            product_id=new_product_id,
            quantity=1,
            proration_billing_mode="difference_immediately"
        )
        
        # Credits are automatically applied to future renewals
        return result

    # Downgrade from Pro ($80) to Basic ($30)
    # $50 credit added to subscription, auto-applied on next renewal
    downgrade = downgrade_subscription("sub_123", "prod_basic_plan")
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    func downgradeSubscription(subscriptionID string, newProductID string) error {
        client := dodopayments.NewClient(
            option.WithBearerToken(os.Getenv("DODO_PAYMENTS_API_KEY")),
        )
        
        // Preview the downgrade first
        preview, err := client.Subscriptions.PreviewChangePlan(
            context.TODO(),
            subscriptionID,
            dodopayments.SubscriptionPreviewChangePlanParams{
                UpdateSubscriptionPlanReq: dodopayments.UpdateSubscriptionPlanReqParam{
                    ProductID:            dodopayments.F(newProductID),
                    Quantity:             dodopayments.F(int64(1)),
                    ProrationBillingMode: dodopayments.F(dodopayments.UpdateSubscriptionPlanReqProrationBillingModeDifferenceImmediately),
                },
            },
        )
        if err != nil {
            return err
        }
        
        fmt.Printf("Customer credits to be applied: %v\n", preview.ImmediateCharge.Summary.CustomerCredits)
        
        // Execute the downgrade (returns no response body; nil error means success)
        return client.Subscriptions.ChangePlan(
            context.TODO(),
            subscriptionID,
            dodopayments.SubscriptionChangePlanParams{
                UpdateSubscriptionPlanReq: dodopayments.UpdateSubscriptionPlanReqParam{
                    ProductID:            dodopayments.F(newProductID),
                    Quantity:             dodopayments.F(int64(1)),
                    ProrationBillingMode: dodopayments.F(dodopayments.UpdateSubscriptionPlanReqProrationBillingModeDifferenceImmediately),
                },
            },
        )
    }
    ```
  </Tab>
</Tabs>

<Info>
  `difference_immediately` का उपयोग करके डाउनग्रेड से प्राप्त क्रेडिट सब्सक्रिप्शन-स्कोप्ड होते हैं और स्वचालित रूप से भविष्य के नवीनीकरणों पर लागू होते हैं। ये [क्रेडिट-आधारित बिलिंग](/features/credit-based-billing) अधिकारों से अलग हैं।
</Info>

## पूर्ण उदाहरण: पोस्ट-खरीद Upsell फ्लो

यहाँ एक सफल खरीद के बाद upsell पेश करने का पूर्ण कार्यान्वयन है:

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    import DodoPayments from 'dodopayments';
    import express from 'express';

    const client = new DodoPayments({
      bearerToken: process.env.DODO_PAYMENTS_API_KEY,
      environment: 'live_mode',
    });

    const app = express();

    // Store for tracking upsell eligibility (use your database in production)
    const eligibleUpsells = new Map<string, { customerId: string; productId: string }>();

    // Webhook handler for initial purchase success
    app.post('/webhooks/dodo', express.raw({ type: 'application/json' }), async (req, res) => {
      const event = JSON.parse(req.body.toString());
      
      switch (event.type) {
        case 'payment.succeeded':
          // Check if customer is eligible for upsell
          const customerId = event.data.customer_id;
          const productId = event.data.product_id;
          
          // Define upsell rules (e.g., bought Basic, offer Pro)
          const upsellProduct = getUpsellProduct(productId);
          
          if (upsellProduct) {
            eligibleUpsells.set(customerId, {
              customerId,
              productId: upsellProduct
            });
          }
          break;
          
        case 'payment.failed':
          console.log('Payment failed:', event.data.payment_id);
          // Handle failed upsell payment
          break;
      }
      
      res.json({ received: true });
    });

    // API endpoint to check upsell eligibility
    app.get('/api/upsell/:customerId', async (req, res) => {
      const { customerId } = req.params;
      const upsell = eligibleUpsells.get(customerId);
      
      if (!upsell) {
        return res.json({ eligible: false });
      }
      
      // Get payment methods
      const methods = await client.customers.retrievePaymentMethods(customerId);
      
      if (methods.length === 0) {
        return res.json({ eligible: false, reason: 'no_payment_method' });
      }
      
      // Get product details for display
      const product = await client.products.retrieve(upsell.productId);
      
      res.json({
        eligible: true,
        product: {
          id: product.product_id,
          name: product.name,
          price: product.price,
          currency: product.currency
        },
        paymentMethodId: methods[0].payment_method_id
      });
    });

    // API endpoint to accept upsell
    app.post('/api/upsell/:customerId/accept', async (req, res) => {
      const { customerId } = req.params;
      const upsell = eligibleUpsells.get(customerId);
      
      if (!upsell) {
        return res.status(400).json({ error: 'No upsell available' });
      }
      
      try {
        const methods = await client.customers.retrievePaymentMethods(customerId);
        
        // Create one-click purchase
        const session = await client.checkoutSessions.create({
          product_cart: [{ product_id: upsell.productId, quantity: 1 }],
          customer: { customer_id: customerId },
          payment_method_id: methods[0].payment_method_id,
          confirm: true,
          return_url: `${process.env.APP_URL}/upsell-success`,
          feature_flags: { redirect_immediately: true },
          metadata: { upsell: 'true', source: 'post_purchase' }
        });
        
        // Clear the upsell offer
        eligibleUpsells.delete(customerId);
        
        res.json({ success: true, sessionId: session.session_id });
      } catch (error) {
        console.error('Upsell failed:', error);
        res.status(500).json({ error: 'Upsell processing failed' });
      }
    });

    // Helper function to determine upsell product
    function getUpsellProduct(purchasedProductId: string): string | null {
      const upsellMap: Record<string, string> = {
        'prod_basic_plan': 'prod_pro_plan',
        'prod_starter_course': 'prod_complete_bundle',
        'prod_single_license': 'prod_team_license'
      };
      
      return upsellMap[purchasedProductId] || null;
    }

    app.listen(3000);
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    import os
    from flask import Flask, request, jsonify
    from dodopayments import DodoPayments

    client = DodoPayments(
        bearer_token=os.environ.get("DODO_PAYMENTS_API_KEY"),
        environment="live_mode",
    )

    app = Flask(__name__)

    # Store for tracking upsell eligibility (use your database in production)
    eligible_upsells = {}

    @app.route('/webhooks/dodo', methods=['POST'])
    def webhook_handler():
        event = request.json
        
        if event['type'] == 'payment.succeeded':
            # Check if customer is eligible for upsell
            customer_id = event['data']['customer_id']
            product_id = event['data']['product_id']
            
            # Define upsell rules
            upsell_product = get_upsell_product(product_id)
            
            if upsell_product:
                eligible_upsells[customer_id] = {
                    'customer_id': customer_id,
                    'product_id': upsell_product
                }
        
        elif event['type'] == 'payment.failed':
            print(f"Payment failed: {event['data']['payment_id']}")
        
        return jsonify({'received': True})

    @app.route('/api/upsell/<customer_id>', methods=['GET'])
    def check_upsell(customer_id):
        upsell = eligible_upsells.get(customer_id)
        
        if not upsell:
            return jsonify({'eligible': False})
        
        # Get payment methods
        methods = client.customers.retrieve_payment_methods(customer_id)
        
        if not methods:
            return jsonify({'eligible': False, 'reason': 'no_payment_method'})
        
        # Get product details for display
        product = client.products.retrieve(upsell['product_id'])
        
        return jsonify({
            'eligible': True,
            'product': {
                'id': product.product_id,
                'name': product.name,
                'price': product.price,
                'currency': product.currency
            },
            'payment_method_id': methods[0].payment_method_id
        })

    @app.route('/api/upsell/<customer_id>/accept', methods=['POST'])
    def accept_upsell(customer_id):
        upsell = eligible_upsells.get(customer_id)
        
        if not upsell:
            return jsonify({'error': 'No upsell available'}), 400
        
        try:
            methods = client.customers.retrieve_payment_methods(customer_id)
            
            # Create one-click purchase
            session = client.checkout_sessions.create(
                product_cart=[{'product_id': upsell['product_id'], 'quantity': 1}],
                customer={'customer_id': customer_id},
                payment_method_id=methods[0].payment_method_id,
                confirm=True,
                return_url=f"{os.environ['APP_URL']}/upsell-success",
                feature_flags={'redirect_immediately': True},
                metadata={'upsell': 'true', 'source': 'post_purchase'}
            )
            
            # Clear the upsell offer
            del eligible_upsells[customer_id]
            
            return jsonify({'success': True, 'session_id': session.session_id})
        
        except Exception as error:
            print(f"Upsell failed: {error}")
            return jsonify({'error': 'Upsell processing failed'}), 500

    def get_upsell_product(purchased_product_id: str) -> str:
        """Determine upsell product based on purchased product."""
        upsell_map = {
            'prod_basic_plan': 'prod_pro_plan',
            'prod_starter_course': 'prod_complete_bundle',
            'prod_single_license': 'prod_team_license'
        }
        return upsell_map.get(purchased_product_id)

    if __name__ == '__main__':
        app.run(port=3000)
    ```
  </Tab>
</Tabs>

## सर्वोत्तम प्रथाएँ

<AccordionGroup>
  <Accordion title="Time Your Upsells Strategically">
    Upsell पेश करने का सबसे अच्छा समय सफल खरीद के तुरंत बाद होता है जब ग्राहक खरीदारी के मनोदशा में होते हैं। अन्य प्रभावी क्षण:

    * फीचर उपयोग की माइलस्टोन के बाद
    * योजना सीमाओं के पास जाते समय
    * ऑनबोर्डिंग पूरा होने पर
  </Accordion>

  <Accordion title="Validate Payment Method Eligibility">
    एक-क्लिक चार्ज करने का प्रयास करने से पहले भुगतान तरीके की पुष्टि करें:

    * क्या यह उत्पाद की मुद्रा के साथ संगत है
    * क्या इसकी अवधि समाप्त नहीं हुई है
    * क्या यह ग्राहक से ही जुड़ा है

    API इन चीज़ों को मान्य करेगा, लेकिन सक्रिय रूप से जांचने से UX सुधरता है।
  </Accordion>

  <Accordion title="Handle Failures Gracefully">
    जब एक-क्लिक चार्ज असफल हों:

    1. मानक चेकआउट फ्लो पर वापस जाएँ
    2. ग्राहक को स्पष्ट संदेश दें
    3. भुगतान तरीका अपडेट करने का विकल्प दें
    4. विफल चार्ज को बार-बार पुनः प्रयास न करें
  </Accordion>

  <Accordion title="Provide Clear Value Proposition">
    जब ग्राहक मूल्य समझते हैं तो upsells बेहतर रूपांतरण करते हैं:

    * वे क्या प्राप्त कर रहे हैं बनाम वर्तमान योजना दिखाएँ
    * कुल कीमत नहीं, मूल्य अंतर को हाइलाइट करें
    * सामाजिक प्रमाण उपयोग करें (उदा., “सबसे लोकप्रिय अपग्रेड”)
  </Accordion>

  <Accordion title="Respect Customer Choice">
    * हमेशा अस्वीकृत करने का आसान तरीका प्रदान करें
    * अस्वीकृति के बाद एक ही upsell बार-बार न दिखाएँ
    * यह ट्रैक और विश्लेषण करें कि किस upsell से रूपांतरण होता है ताकि ऑफ़र अनुकूलित हों
  </Accordion>
</AccordionGroup>

## मॉनिटर करने के लिए वेबहुक्स

Upsell और डाउनग्रेड फ्लो के लिए इन वेबहुक इवेंट्स को ट्रैक करें:

| इवेंट                       | ट्रिगर                                       | क्रिया                                             |
| --------------------------- | -------------------------------------------- | -------------------------------------------------- |
| `payment.succeeded`         | Upsell/क्रॉस-सेल भुगतान पूरा हुआ             | उत्पाद वितरित करें, पहुंच अपडेट करें               |
| `payment.failed`            | एक-क्लिक चार्ज असफल रहा                      | त्रुटि दिखाएँ, पुनः प्रयास या फॉलबैक का विकल्प दें |
| `subscription.plan_changed` | अपग्रेड/डाउनग्रेड पूरा हुआ                   | फीचर्स अपडेट करें, पुष्टि भेजें                    |
| `subscription.active`       | योजना परिवर्तन के बाद सदस्यता फिर सक्रिय हुई | नए स्तर तक पहुंच दें                               |

<Card title="Webhook Integration Guide" icon="webhook" href="/developer-resources/webhooks">
  वेबहुक एंडपॉइंट कैसे सेट करें और सत्यापित करें जानें।
</Card>

## संबंधित संसाधन

<CardGroup cols={2}>
  <Card title="Subscription Upgrade Guide" icon="arrows-rotate" href="/developer-resources/subscription-upgrade-downgrade">
    योजना परिवर्तनों, प्रोरैशन मोड, और असफलताओं को संभालने पर विस्तृत गाइड।
  </Card>

  <Card title="Checkout Sessions" icon="cart-shopping" href="/developer-resources/checkout-session">
    सभी विकल्पों के साथ चेकआउट सत्र बनाने के लिए संपूर्ण संदर्भ।
  </Card>

  <Card title="Customer Payment Methods API" icon="credit-card" href="/api-reference/customers/get-customer-payment-methods">
    ग्राहक भुगतान तरीकों की सूची बनाने के लिए API संदर्भ।
  </Card>

  <Card title="Add-ons" icon="puzzle-piece" href="/features/addons">
    अतिरिक्त राजस्व के लिए लचीले ऐड-ऑन के साथ सदस्यताओं को बढ़ाएँ।
  </Card>
</CardGroup>
