跳转到主要内容
动态定价允许您为产品提供可变定价,而无需创建多个产品条目。通过在单一产品上启用随意支付 (PWYW),您可以设置最低和最高价格范围,然后在创建结账会话链接时传递动态金额。 这种方法非常适合您需要的情况:
  • 可变定价,无需管理多个产品
  • 客户驱动定价,让买家选择他们的金额
  • 编程价格控制,通过 API 动态设置金额
  • 灵活的定价模型,适用于数字产品、捐赠或实验性发布
Pay What You Want 仅适用于Single Payment(一次性)产品,不能用于订阅产品。

工作原理

启用随意支付后,您可以:
  1. 设置价格范围:定义最低价格(必填),可选地设置最高价格
  2. 传递动态金额:在创建结账会话时,在产品购物车中包含 amount 字段
  3. 让客户选择:如果未提供金额,客户可以输入自己的价格(在您的范围内)
当您在产品购物车中传递 amount 时,该金额将用于结账。如果省略 amount 字段,客户可以在结账时自行选择价格(受最低/最高设置限制)。

第一步:创建一个随意支付的产品

首先,在您的 Dodo Payments 仪表板中创建一个一次性产品,并启用随意支付定价。
1

Create a new product

在 Dodo Payments 仪表板中导航到 Products 并点击 Add Product
2

Configure product details

填写所需的产品信息:
  • Product Name:产品的显示名称
  • Product Description:清楚描述客户所购买的内容
  • Product Image:上传图片(PNG/JPG/WebP,最多 3 MB)
  • Tax Category:选择适当的税务类别
3

Set pricing type

Pricing Type 选择为 Single Payment(一次性付款)。
4

Enable Pay What You Want

Pricing 部分启用 Pay What You Want 切换按钮。
5

Set minimum price

输入客户必须支付的最低价格。这是必填项,可确保您保持收入底线。示例:如果最低价格为 $5.00,请输入 5.00(或 500 美分)。
6

Set maximum price (optional)

可选地设置最高价格,以限制客户支付的金额。
7

Set suggested price (optional)

可选地输入建议价格,以向客户展示,引导期望并可能提高平均订单价值。
8

Save the product

点击 Add Product 以保存。记下您的产品 ID(例如 pdt_123abc456def),以在结账会话中使用。
您可以在仪表板的 ProductsView Details 中找到产品 ID,或使用 List Products API

第二步:创建具有动态定价的结账会话

一旦您的产品配置了 Pay What You Want,即可创建带动态金额的结账会话。产品购物车中的 amount 字段允许您为每个结账会话以编程方式设置价格。

理解金额字段

在创建结账会话时,您可以在每个产品购物车项中包含 amount 字段:
  • 如果提供了 amount:结账将使用该精确金额(必须在您的最低/最高范围内)
  • 如果省略 amount:客户可以在结账时输入自己的价格(在您的范围内)
amount 字段仅适用于 Pay What You Want 产品。对于常规产品,该字段会被忽略。

代码示例

import DodoPayments from 'dodopayments';

// Initialize the Dodo Payments client
const client = new DodoPayments({
  bearerToken: process.env.DODO_PAYMENTS_API_KEY,
});

async function createDynamicPricingCheckout(
  productId: string,
  amountInCents: number,
  returnUrl: string
) {
  try {
    const session = await client.checkoutSessions.create({
      product_cart: [
        {
          product_id: productId,
          quantity: 1,
          // Dynamic amount in cents (e.g., 1500 = $15.00)
          amount: amountInCents
        }
      ],
      return_url: returnUrl,
      // Optional: Pre-fill customer information
      customer: {
        email: 'customer@example.com',
        name: 'John Doe'
      },
      // Optional: Add metadata for tracking
      metadata: {
        order_id: 'order_123',
        pricing_tier: 'custom'
      }
    });

    console.log('Checkout URL:', session.checkout_url);
    console.log('Session ID:', session.session_id);
    
    return session;
  } catch (error) {
    console.error('Failed to create checkout session:', error);
    throw error;
  }
}

// Example: Create checkout with $25.00 (2500 cents)
const session = await createDynamicPricingCheckout(
  'prod_123abc456def',
  2500, // $25.00 in cents
  'https://yoursite.com/checkout/success'
);

// Example: Create checkout with $10.00 (1000 cents)
const session2 = await createDynamicPricingCheckout(
  'prod_123abc456def',
  1000, // $10.00 in cents
  'https://yoursite.com/checkout/success'
);
金额格式amount 字段必须使用该货币的最小单位。对于 USD,这意味着美分(例如 $25.00 = 2500)。对于其他货币,则使用最小计量单位(例如 INR 的 paise)。

第三步:让客户选择他们的价格

如果您希望客户在结账时自行选择价格,只需在产品购物车中省略 amount 字段。结账页面会显示一个输入框,让客户输入任何在您最低和最高范围之间的金额。
async function createCustomerChoiceCheckout(
  productId: string,
  returnUrl: string
) {
  try {
    const session = await client.checkoutSessions.create({
      product_cart: [
        {
          product_id: productId,
          quantity: 1
          // No amount field - customer will choose their price
        }
      ],
      return_url: returnUrl,
      customer: {
        email: 'customer@example.com',
        name: 'John Doe'
      }
    });

    return session;
  } catch (error) {
    console.error('Failed to create checkout session:', error);
    throw error;
  }
}

常见用例

用例 1:基于用户类型的分层定价

为不同客户群体提供不同价格,使用相同的产品:
// Student discount: $10.00
const studentSession = await createDynamicPricingCheckout(
  'prod_123abc456def',
  1000, // $10.00
  'https://yoursite.com/success'
);

// Regular price: $25.00
const regularSession = await createDynamicPricingCheckout(
  'prod_123abc456def',
  2500, // $25.00
  'https://yoursite.com/success'
);

// Premium price: $50.00
const premiumSession = await createDynamicPricingCheckout(
  'prod_123abc456def',
  5000, // $50.00
  'https://yoursite.com/success'
);

用例 2:基于数量的动态定价

根据购买数量调整价格:
async function createQuantityBasedCheckout(
  productId: string,
  quantity: number
) {
  // Base price: $20.00 per unit
  // Discount: 10% for 2+ items, 20% for 5+ items
  const basePrice = 2000; // $20.00 in cents
  let discount = 0;
  
  if (quantity >= 5) {
    discount = 0.20; // 20% off
  } else if (quantity >= 2) {
    discount = 0.10; // 10% off
  }
  
  const totalAmount = Math.round(basePrice * quantity * (1 - discount));
  
  const session = await client.checkoutSessions.create({
    product_cart: [
      {
        product_id: productId,
        quantity: quantity,
        amount: totalAmount
      }
    ],
    return_url: 'https://yoursite.com/success'
  });
  
  return session;
}

用例 3:基于时间或促销的定价

在特定时期内应用促销定价:
async function createPromotionalCheckout(productId: string) {
  const isPromoActive = checkIfPromotionActive(); // Your logic
  const regularPrice = 3000; // $30.00
  const promoPrice = 2000; // $20.00
  
  const amount = isPromoActive ? promoPrice : regularPrice;
  
  const session = await client.checkoutSessions.create({
    product_cart: [
      {
        product_id: productId,
        quantity: 1,
        amount: amount
      }
    ],
    return_url: 'https://yoursite.com/success',
    metadata: {
      pricing_type: isPromoActive ? 'promotional' : 'regular'
    }
  });
  
  return session;
}

最佳实践

Set Reasonable Bounds

选择一个既能覆盖成本又让客户可接受的最低价格。使用建议价格来引导客户期望。

Validate Amounts

在创建结账会话前,请始终验证动态金额是否在产品的最低和最高范围内。

Track Pricing Decisions

使用 metadata 跟踪选择特定金额的原因(例如 pricing_tierdiscount_codeuser_segment)。

Handle Edge Cases

确保您的应用能优雅地处理金额超出最高或低于最低的情况。

验证和错误处理

始终根据您产品的最低和最高设置验证金额:
async function createValidatedCheckout(
  productId: string,
  amountInCents: number,
  minAmount: number,
  maxAmount: number | null
) {
  // Validate minimum
  if (amountInCents < minAmount) {
    throw new Error(
      `Amount ${amountInCents} is below minimum ${minAmount}`
    );
  }
  
  // Validate maximum (if set)
  if (maxAmount !== null && amountInCents > maxAmount) {
    throw new Error(
      `Amount ${amountInCents} exceeds maximum ${maxAmount}`
    );
  }
  
  // Create checkout session
  return await client.checkoutSessions.create({
    product_cart: [
      {
        product_id: productId,
        quantity: 1,
        amount: amountInCents
      }
    ],
    return_url: 'https://yoursite.com/success'
  });
}

API 参考

故障排除

如果 amount 字段被忽略,请确认:
  • 该产品在仪表板中已启用 Pay What You Want
  • 该产品是Single Payment(一次性)产品,而非订阅
  • 金额格式正确(使用货币的最小单位,例如 USD 的美分)
如果金额违反产品的价格范围,API 会拒绝结账会话。在创建会话前务必验证金额,或通过省略 amount 字段,让客户自行选择价格。
如果客户无法看到价格输入字段,请确保在产品购物车中省略了 amount 字段。当提供该字段时,结账会使用该精确金额。