Zum Hauptinhalt springen
Upsells und Downsells ermöglichen es Ihnen, zusätzlichen Produkte oder Planänderungen den Kunden über ihre gespeicherten Zahlungsmethoden anzubieten. Dies ermöglicht Einkäufe per Klick, die die Zahlungserhebung überspringen und die Konversionsraten erheblich verbessern.

Post-Purchase Upsells

Bieten Sie ergänzende Produkte sofort nach dem Checkout mit Einkäufen per Klick an.

Subscription Upgrades

Versetzen Sie Kunden automatisch in höhere Tarifstufen mit automatischer Proration und sofortiger Abrechnung.

Cross-Sells

Fügen Sie bestehenden Kunden verwandte Produkte hinzu, ohne Zahlungsdetails erneut einzugeben.

Übersicht

Upsells und Downsells sind leistungsstarke Strategien zur Umsatzoptimierung:
  • Upsells: Bieten Sie ein höherwertiges Produkt oder Upgrade an (z.B. Pro-Plan anstelle von Basic)
  • Downsells: Bieten Sie eine günstigere Alternative an, wenn ein Kunde ablehnt oder downgradiert
  • Cross-sells: Schlagen Sie ergänzende Produkte vor (z.B. Add-ons, verwandte Artikel)
Dodo Payments ermöglicht diese Abläufe durch den payment_method_id Parameter, der es Ihnen ermöglicht, die gespeicherte Zahlungsmethode eines Kunden zu belasten, ohne dass er die Kartendetails erneut eingeben muss.

Wesentliche Vorteile

VorteilEinfluss
Einkäufe per KlickZahlungformular für wiederkehrende Kunden vollständig überspringen
Höhere KonversionVerringern Sie die Reibung im Entscheidungs-Moment
Sofortige VerarbeitungBelastungen werden sofort mit confirm: true verarbeitet
Nahtlose BenutzererfahrungKunden bleiben während des gesamten Prozesses in Ihrer App

Funktionsweise

Voraussetzungen

Bevor Sie Upsells und Downsells implementieren, stellen Sie sicher, dass Sie:
1

Kunde mit gespeicherter Zahlungsmethode

Kunden müssen mindestens einen Einkauf abgeschlossen haben. Zahlungsmethoden werden automatisch gespeichert, wenn Kunden den Checkout abschließen.
2

Produkte konfiguriert

Erstellen Sie Ihre Upsell-Produkte im Dodo Payments Dashboard. Diese können Einmalzahlungen, Abonnements oder Add-ons sein.
3

Webhook-Endpunkt

Richten Sie Webhooks ein, um payment.succeeded, payment.failed und subscription.plan_changed Ereignisse zu verarbeiten.

Abrufen der Zahlungsmethoden von Kunden

Bevor Sie ein Upsell anbieten, rufen Sie die gespeicherten Zahlungsmethoden des Kunden ab:
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.listPaymentMethods(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;
Zahlungsmethoden werden automatisch gespeichert, wenn Kunden den Checkout abschließen. Sie müssen sie nicht explizit speichern.

Post-Purchase One-Click Upsells

Bieten Sie zusätzliche Produkte sofort nach einem erfolgreichen Kauf an. Der Kunde kann mit einem einzigen Klick akzeptieren, da seine Zahlungsmethode bereits gespeichert ist.

Implementierung

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.listPaymentMethods(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;
}
Beim Verwenden von payment_method_id müssen Sie confirm: true festlegen und eine vorhandene customer_id bereitstellen. Die Zahlungsmethode muss zu diesem Kunden gehören.

Subscription Upgrades

Versetzen Sie Kunden automatisch in höherwertige Abonnementpläne mit automatischer Prorationsbehandlung.

Vorschau vor der Bestätigung

Zeigen Sie immer die Planänderungen in der Vorschau an, um Kunden genau zu zeigen, was sie berechnet werden:
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}`);

Durchführung des Upgrades

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);

Prorationsmodi

Wählen Sie, wie Kunden bei einem Upgrade berechnet werden:
ModusVerhaltenAm besten geeignet für
difference_immediatelyBerechnet Preisunterschied sofort (3030→80 = $50)Einfache Upgrades
prorated_immediatelyBerechnet auf Basis der verbleibenden Zeit im AbrechnungszyklusFaire zeitbasierte Abrechnung
full_immediatelyBerechnet den vollständigen Preis des neuen Plans, ignoriert verbleibende ZeitAbrechnungszyklus wird zurückgesetzt
Verwenden Sie difference_immediately für unkomplizierte Upgrade-Abläufe. Verwenden Sie prorated_immediately, wenn Sie ungenutzte Zeit im aktuellen Plan berücksichtigen möchten.

Cross-Sells

Fügen Sie komplementäre Produkte für bestehende Kunden hinzu, ohne dass diese die Zahlungsdetails erneut eingeben müssen.

Implementierung

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.listPaymentMethods(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);
}

Subscription Downgrades

Wenn Kunden zu einem niedrigeren Plan wechseln möchten, gestalten Sie den Übergang reibungslos mit automatischen Gutschriften.

So funktionieren Downgrades

  1. Kunde beantragt Downgrade (Pro → Basic)
  2. System berechnet den verbleibenden Wert des aktuellen Plans
  3. Gutschrift wird dem Abonnement für zukünftige Erneuerungen hinzugefügt
  4. Kunde wechselt sofort zu einem neuen Plan
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');
Gutschriften aus Downgrades mithilfe von difference_immediately sind abonnementgebunden und werden automatisch bei zukünftigen Erneuerungen angewendet. Sie unterscheiden sich von Kundengutschrift.

Komplettes Beispiel: Post-Purchase Upsell-Flow

Hier ist eine vollständige Implementierung, die zeigt, wie man ein Upsell nach einem erfolgreichen Kauf anbietet:
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.listPaymentMethods(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.listPaymentMethods(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);

Best Practices

Der beste Zeitpunkt, um ein Upsell anzubieten, ist sofort nach einem erfolgreichen Kauf, wenn die Kunden in einer Kaufmentalität sind. Weitere effektive Momente:
  • Nach der Nutzung von Funktionen
  • Wenn sich die Tarifgrenzen nähern
  • Bei Abschluss der Onboarding-Phase
Überprüfen Sie vor dem Versuch eines Einkaufs per Klick die Zahlungsmethode:
  • Ist sie mit der Währung des Produkts kompatibel
  • Ist sie nicht abgelaufen
  • Gehört sie dem Kunden
Die API wird diese validieren, aber eine proaktive Überprüfung verbessert die Benutzererfahrung.
Wenn Einkäufe per Klick fehlschlagen:
  1. Fällt auf den Standard-Checkout-Prozess zurück
  2. Benachrichtigen Sie den Kunden mit klaren Nachrichten
  3. Bieten Sie an, die Zahlungsmethode zu aktualisieren
  4. Versuchen Sie nicht wiederholt fehlgeschlagene Zahlungen
Upsells konvertieren besser, wenn Kunden den Wert verstehen:
  • Zeigen Sie, was sie im Vergleich zum aktuellen Plan erhalten
  • Heben Sie den Preisunterschied hervor, nicht den Gesamtpreis
  • Verwenden Sie sozialen Beweis (z.B. “Beliebtestes Upgrade”)
  • Bieten Sie immer eine einfache Möglichkeit zur Ablehnung an
  • Zeigen Sie nach einer Ablehnung nicht dasselbe Upsell erneut an
  • Verfolgen und analysieren Sie, welche Upsells konvertiert werden, um Angebote zu optimieren

Webhooks zur Überwachung

Verfolgen Sie diese Webhook-Ereignisse für Upsell- und Downgrade-Flows:
EreignisAuslöserAktion
payment.succeededZahlung für Upsell/Cross-Sell abgeschlossenProdukt liefern, Zugang aktualisieren
payment.failedFehlgeschlagener EinkaufsversuchFehler anzeigen, erneut versuchen oder zurückfallen anbieten
subscription.plan_changedUpgrade/Downgrade abgeschlossenFunktionen aktualisieren, Bestätigung senden
subscription.activeAbonnement nach Planänderung reaktiviertZugriff auf neue Stufe gewähren

Webhook-Integrationsleitfaden

Erfahren Sie, wie Sie Webhook-Endpunkte einrichten und verifizieren.

Verwandte Ressourcen