Chuyển đến nội dung chính

Change Plan API

Full API docs for updating subscriptions.

Plan Change Preview

See charge amounts before changing plans.

Integration Guide

Step-by-step subscription setup.

What is a subscription upgrade or downgrade?

Changing plans lets you move a customer between subscription tiers or quantities. Use it to:
  • Align pricing with usage or features
  • Move from monthly to annual (or vice versa)
  • Adjust quantity for seat-based products
Plan changes can trigger an immediate charge depending on the proration mode you choose.

When to use plan changes

  • Upgrade when a customer needs more features, usage, or seats
  • Downgrade when usage decreases
  • Migrate users to a new product or price without cancelling their subscription

Plan Change Flow

Prerequisites

Before implementing subscription plan changes, ensure you have:
  • A Dodo Payments merchant account with active subscription products
  • API credentials (API key and webhook secret key) from the dashboard
  • An existing active subscription to modify
  • Webhook endpoint configured to handle subscription events
For detailed setup instructions, see our Integration Guide.

Step-by-Step Implementation Guide

Follow this comprehensive guide to implement subscription plan changes in your application:
1

Understand Plan Change Requirements

Before implementing, determine:
  • Which subscription products can be changed to which others
  • What proration mode fits your business model
  • How to handle failed plan changes gracefully
  • Which webhook events to track for state management
Test plan changes thoroughly in test mode before implementing in production.
2

Choose Your Proration Strategy

Select the billing approach that aligns with your business needs:
Best for: SaaS applications wanting to charge fairly for unused time
  • Calculates exact prorated amount based on remaining cycle time
  • Charges a prorated amount based on unused time remaining in the cycle
  • Provides transparent billing to customers
3

Implement the Change Plan API

Use the Change Plan API to modify subscription details:
subscription_id
string
bắt buộc
The ID of the active subscription to modify.
product_id
string
bắt buộc
The new product ID to change the subscription to.
quantity
integer
mặc định:"1"
Number of units for the new plan (for seat-based products).
proration_billing_mode
string
bắt buộc
How to handle immediate billing: prorated_immediately, full_immediately, difference_immediately, or do_not_bill.
addons
array
Optional addons for the new plan. Leaving this empty removes any existing addons.
on_payment_failure
string
Controls behavior when the plan change payment fails:
  • prevent_change: Keep subscription on current plan until payment succeeds
  • apply_change (default): Apply plan change immediately regardless of payment outcome
If not specified, uses the business-level default setting.
discount_code
string
Mã giảm giá tùy chọn để áp dụng cho kế hoạch mới. Nếu được cung cấp, xác thực và áp dụng giảm giá cho thay đổi kế hoạch. Nếu không cung cấp và gói đăng ký đã có mã giảm giá với preserve_on_plan_change=true, mã giảm giá hiện có sẽ được giữ lại (nếu áp dụng cho sản phẩm mới).
effective_at
string
mặc định:"immediately"
Khi nào áp dụng thay đổi kế hoạch:
  • immediately (mặc định): Áp dụng thay đổi kế hoạch ngay lập tức
  • next_billing_date: Lên lịch thay đổi cho ngày thanh toán tiếp theo. Khách hàng giữ lại kế hoạch hiện tại cho đến khi kết thúc kỳ thanh toán.
Sử dụng next_billing_date cho các nâng cấp xuống để khách hàng giữ lại lợi ích của kế hoạch hiện tại cho đến khi kết thúc kỳ thanh toán.
4

Handle Webhook Events

Thiết lập xử lý webhook để theo dõi kết quả thay đổi kế hoạch:
  • subscription.active: Thay đổi kế hoạch thành công, gói đăng ký được cập nhật
  • subscription.plan_changed: Gói đăng ký thay đổi (nâng cấp/giảm cấp/cập nhật addon)
  • subscription.on_hold: Thay đổi kế hoạch tính phí thất bại, tạm dừng gói đăng ký
  • payment.succeeded: Tính phí ngay lập tức cho thay đổi kế hoạch thành công
  • payment.failed: Tính phí ngay lập tức thất bại
Luôn xác minh chữ ký webhook và triển khai xử lý sự kiện idempotent.
5

Update Your Application State

Dựa trên sự kiện webhook, cập nhật ứng dụng của bạn:
  • Cấp/phủ nhận tính năng dựa trên kế hoạch mới
  • Cập nhật bảng điều khiển khách hàng với chi tiết kế hoạch mới
  • Gửi email xác nhận về thay đổi kế hoạch
  • Ghi lại thay đổi thanh toán cho mục đích kiểm toán
6

Test and Monitor

Thử nghiệm kỹ lưỡng việc triển khai của bạn:
  • Kiểm tra tất cả các chế độ tỷ lệ ứng với các kịch bản khác nhau
  • Xác minh xử lý webhook hoạt động chính xác
  • Giám sát tỷ lệ thành công của thay đổi kế hoạch
  • Thiết lập cảnh báo cho những thay đổi kế hoạch thất bại
Việc triển khai thay đổi kế hoạch đăng ký của bạn đã sẵn sàng để sử dụng cho sản xuất.

Xem trước Thay đổi Kế hoạch

Trước khi cam kết thay đổi kế hoạch, sử dụng API Xem Trước để hiển thị chính xác những gì khách hàng sẽ bị tính phí:
const preview = await client.subscriptions.previewChangePlan('sub_123', {
  product_id: 'prod_pro',
  quantity: 1,
  proration_billing_mode: 'prorated_immediately'
});

// Show customer the charge before confirming
console.log('Immediate charge:', preview.immediate_charge.summary);
console.log('New plan details:', preview.new_plan);
Sử dụng API xem trước để xây dựng các hộp thoại xác nhận hiển thị cho khách hàng số tiền chính xác họ sẽ bị tính phí trước khi họ xác nhận thay đổi kế hoạch.

API Thay đổi Kế hoạch

Sử dụng API Thay đổi Kế hoạch để thay đổi sản phẩm, số lượng và hành vi tỷ lệ của một gói đăng ký đang hoạt động.

Ví dụ bắt đầu nhanh

import DodoPayments from 'dodopayments';

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

async function changePlan() {
  const result = await client.subscriptions.changePlan('sub_123', {
    product_id: 'prod_new',
    quantity: 3,
    proration_billing_mode: 'prorated_immediately',
    on_payment_failure: 'prevent_change', // Optional: control behavior on payment failure
  });
  console.log(result.status, result.invoice_id, result.payment_id);
}

changePlan();
Success
{
  "status": "processing",
  "subscription_id": "sub_123",
  "invoice_id": "inv_789",
  "payment_id": "pay_456",
  "proration_billing_mode": "prorated_immediately"
}
Các trường như invoice_idpayment_id chỉ được trả về khi một khoản phí và/hoặc hóa đơn được tạo trong quá trình thay đổi kế hoạch. Luôn dựa vào các sự kiện webhook (ví dụ: payment.succeeded, subscription.plan_changed) để xác nhận kết quả.
Nếu khoản phí ngay lập tức thất bại, gói đăng ký có thể chuyển sang subscription.on_hold cho đến khi thanh toán thành công.

Quản lý Addons

Khi thay đổi gói đăng ký, bạn cũng có thể điều chỉnh addons:
// Add addons to the new plan
await client.subscriptions.changePlan('sub_123', {
  product_id: 'prod_new',
  quantity: 1,
  proration_billing_mode: 'difference_immediately',
  addons: [
    { addon_id: 'addon_123', quantity: 2 }
  ]
});

// Remove all existing addons
await client.subscriptions.changePlan('sub_123', {
  product_id: 'prod_new',
  quantity: 1,
  proration_billing_mode: 'difference_immediately',
  addons: [] // Empty array removes all existing addons
});
Addons được bao gồm trong tính toán tỷ lệ và sẽ được tính theo chế độ tỷ lệ đã chọn.

Áp dụng Mã Giảm Giá

Bạn có thể áp dụng mã giảm giá khi thay đổi gói đăng ký. Điều này hữu ích để cung cấp giá khuyến mãi cho nâng cấp hoặc di chuyển.
// Apply a discount code during plan change
await client.subscriptions.changePlan('sub_123', {
  product_id: 'prod_pro',
  quantity: 1,
  proration_billing_mode: 'prorated_immediately',
  discount_code: 'UPGRADE20'
});

Hành vi mã giảm giá khi thay đổi kế hoạch

Kịch bảnHành vi
discount_code được cung cấpXác thực và áp dụng giảm giá cho kế hoạch mới.
Không có mã cung cấp, mã giảm giá hiện có với preserve_on_plan_change=trueMã giảm giá hiện có được tự động giữ nguyên nếu áp dụng cho sản phẩm mới.
Không có mã cung cấp, không có mã giảm giá bảo lưuKhông có mã giảm giá nào được áp dụng cho kế hoạch mới.
Sử dụng Preview Plan Change API với một discount_code để hiển thị cho khách hàng chính xác số tiền họ tiết kiệm trước khi xác nhận thay đổi kế hoạch.

Chế độ tính tỷ lệ

Chọn cách để thanh toán khách hàng khi thay đổi gói:

prorated_immediately

  • Tính phí cho sự chênh lệch phần còn lại trong chu kỳ hiện tại
  • Nếu đang trong giai đoạn thử nghiệm, tính phí ngay và chuyển sang gói mới ngay lập tức
  • Hạ cấp: có thể tạo ra một khoản tín dụng tỷ lệ áp dụng cho các gia hạn tương lai

full_immediately

  • Tính phí đầy đủ cho gói mới ngay lập tức
  • Bỏ qua thời gian còn lại từ gói cũ
Các khoản tín dụng được tạo ra bởi các lần hạ cấp sử dụng difference_immediately là thuộc phạm vi gói đăng ký và khác biệt với các quyền lợi Thanh toán dựa trên tín dụng. Chúng tự động áp dụng cho các gia hạn tương lai của cùng một gói đăng ký và không thể chuyển giữa các gói đăng ký.

difference_immediately

  • Nâng cấp: tính phí ngay lập tức chênh lệch giá giữa gói cũ và mới
  • Hạ cấp: thêm giá trị còn lại dưới dạng tín dụng nội bộ vào gói đăng ký và tự động áp dụng cho các lần gia hạn

do_not_bill

  • Không tính phí hoặc tín dụng
  • Khách hàng chuyển ngay sang gói mới mà không cần điều chỉnh thanh toán
  • Chu kỳ thanh toán không thay đổi
  • Tốt cho các lần di chuyển lịch sự, chuyển đổi gói miễn phí, hoặc hấp thụ chênh lệch chi phí
Tính năngprorated_immediatelydifference_immediatelyfull_immediatelydo_not_bill
Phí nâng cấpChênh lệch tỷ lệ cho các ngày còn lạiChênh lệch giá đầy đủ giữa các kế hoạchGiá đầy đủ cho gói mớiKhông tính phí
Tín dụng hạ cấpTín dụng tỷ lệ cho các ngày còn lạiChênh lệch giá đầy đủ dưới dạng tín dụngKhông có tín dụngKhông có tín dụng
Chu kỳ thanh toánKhông thay đổiKhông thay đổiĐặt lại vào hôm nayKhông thay đổi
Hành vi thử nghiệmKết thúc thử nghiệm, tính phí ngayKết thúc thử nghiệm, tính phí ngayKết thúc thử nghiệm, tính phí đầy đủKết thúc thử nghiệm, không tính phí
Tốt nhất choThanh toán công bằng theo thời gianTính toán nâng cấp/hạ cấp đơn giảnĐặt lại chu kỳ thanh toánDi chuyển miễn phí hoặc chuyển đổi lịch sự
Phức tạpTrung bình (tính toán ngày)Thấp (trừ đơn giản)Thấp (tính phí đầy đủ)Không

Kịch bản ví dụ

Sử dụng các con số tiêu chuẩn này nhất quán:
  • Gói hiện tại: Basic với $30/tháng
  • Mục tiêu nâng cấp: Pro với $80/tháng
  • Mục tiêu hạ cấp (từ Pro): Starter với $20/tháng
  • Chu kỳ thanh toán: 30 ngày, bắt đầu vào ngày 1 tháng 1
  • Thay đổi kế hoạch diễn ra vào ngày 16 tháng 1 (15 ngày còn lại, 15 ngày đã sử dụng)
Step 1: Calculate unused credit from current plan
  Unused days = 15 out of 30 days
  Credit = $30 × (15/30) = $15.00

Step 2: Calculate prorated cost of new plan
  Remaining days = 15 out of 30 days
  New plan cost = $80 × (15/30) = $40.00

Step 3: Calculate immediate charge
  Charge = New plan cost − Credit
  Charge = $40.00 − $15.00 = $25.00

→ Customer pays $25.00 now
→ Next renewal (Feb 1): $80.00/month
await client.subscriptions.changePlan('sub_123', {
  product_id: 'prod_pro',
  quantity: 1,
  proration_billing_mode: 'prorated_immediately'
})
Step 1: Calculate unused credit from current plan
  Unused days = 15 out of 30 days
  Credit = $80 × (15/30) = $40.00

Step 2: Calculate prorated cost of new plan
  Remaining days = 15 out of 30 days
  New plan cost = $20 × (15/30) = $10.00

Step 3: Calculate credit balance
  Credit = $40.00 − $10.00 = $30.00

→ No charge — $30.00 credit added to subscription
→ Credit auto-applies to future renewals
→ Next renewal (Feb 1): $20.00 − $30.00 credit = $0.00
→ Following renewal (Mar 1): $20.00 − $10.00 remaining credit = $10.00
await client.subscriptions.changePlan('sub_123', {
  product_id: 'prod_starter',
  quantity: 1,
  proration_billing_mode: 'prorated_immediately'
})
Immediate charge = New plan price − Old plan price
                 = $80 − $30
                 = $50.00

→ Customer pays $50.00 now (regardless of cycle position)
→ Next renewal (Feb 1): $80.00/month
await client.subscriptions.changePlan('sub_123', {
  product_id: 'prod_pro',
  quantity: 1,
  proration_billing_mode: 'difference_immediately'
})
Credit = Old plan price − New plan price
       = $80 − $20
       = $60.00

→ No charge — $60.00 credit added to subscription
→ Credit auto-applies to future renewals
→ Next renewal: $20.00 − $20.00 (from credit) = $0.00
→ Following renewal: $20.00 − $20.00 (from credit) = $0.00
→ Third renewal: $20.00 − $20.00 (from remaining credit) = $0.00
await client.subscriptions.changePlan('sub_123', {
  product_id: 'prod_starter',
  quantity: 1,
  proration_billing_mode: 'difference_immediately'
})
Immediate charge = Full new plan price = $80.00

→ Customer pays $80.00 now
→ No credit for unused time on old plan
→ Billing cycle resets to today (January 16)
→ Next renewal: February 16 at $80.00/month
await client.subscriptions.changePlan('sub_123', {
  product_id: 'prod_pro',
  quantity: 1,
  proration_billing_mode: 'full_immediately'
})
Current: Basic plan ($30/month), no add-ons
New: Pro plan ($80/month) + Extra Seats add-on ($10/seat × 3 seats = $30/month)
Change on day 16 of 30 (15 days remaining)

Step 1: Credit from current plan
  Credit = $30 × (15/30) = $15.00

Step 2: Prorated cost of new plan + add-ons
  New plan = $80 × (15/30) = $40.00
  Add-ons = $30 × (15/30) = $15.00
  Total new = $55.00

Step 3: Immediate charge
  Charge = $55.00 − $15.00 = $40.00

→ Customer pays $40.00 now
→ Next renewal: $80.00 + $30.00 = $110.00/month
await client.subscriptions.changePlan('sub_123', {
  product_id: 'prod_pro',
  quantity: 1,
  proration_billing_mode: 'prorated_immediately',
  addons: [
    { addon_id: 'addon_seats', quantity: 3 }
  ]
})

Cách mỗi chế độ xử lý thanh toán

Chọn prorated_immediately cho tính toán công bằng theo thời gian; chọn full_immediately để khởi động lại chu kỳ thanh toán; sử dụng difference_immediately cho nâng cấp đơn giản và tín dụng tự động khi hạ cấp; hoặc sử dụng do_not_bill để chuyển đổi kế hoạch mà không cần điều chỉnh thanh toán.

Xử lý sự cố Thanh toán

Kiểm soát những gì xảy ra khi thanh toán thay đổi kế hoạch thất bại bằng cách sử dụng tham số on_payment_failure.

Chế độ Xử lý Sự cố Thanh toán

Nếu không xác định, tham số on_payment_failure sử dụng cài đặt mặc định của cấp doanh nghiệp của bạn được cấu hình trong bảng điều khiển.

Khi nào nên Sử dụng Mỗi Chế độ

Kịch bảnChế độ Khuyến nghịLý do
Nâng cấp lên các tính năng cao cấpprevent_changeĐảm bảo thanh toán trước khi cấp quyền truy cập
Tăng số lượng (nhiều chỗ ngồi hơn)prevent_changeNgăn chặn sử dụng mà không thanh toán
Hạ cấp kế hoạchapply_changeKhách hàng đang giảm chi tiêu
Khách hàng doanh nghiệp tin cậyapply_changeRủi ro không thanh toán thấp hơn
Chuyển đổi từ thử nghiệm sang trả phíprevent_changeThời điểm thanh toán quan trọng

Xử lý webhook

Theo dõi trạng thái gói đăng ký thông qua webhook để xác nhận thay đổi kế hoạch và thanh toán.

Các loại sự kiện cần xử lý

  • subscription.active: gói đăng ký được kích hoạt
  • subscription.plan_changed: gói đăng ký đã thay đổi (nâng cấp/hạ cấp/thay đổi addon)
  • subscription.on_hold: tính phí thất bại, gói đăng ký tạm dừng
  • subscription.renewed: gia hạn thành công
  • payment.succeeded: thanh toán cho thay đổi kế hoạch hoặc gia hạn thành công
  • payment.failed: thanh toán thất bại
Chúng tôi khuyến nghị dẫn dắt logic kinh doanh từ sự kiện gói đăng ký và sử dụng sự kiện thanh toán để xác nhận và điều chỉnh.

Xác nhận chữ ký và xử lý ý định

import { NextRequest, NextResponse } from 'next/server';

export async function POST(req) {
  const webhookId = req.headers.get('webhook-id');
  const webhookSignature = req.headers.get('webhook-signature');
  const webhookTimestamp = req.headers.get('webhook-timestamp');
  const secret = process.env.DODO_WEBHOOK_SECRET;

  const payload = await req.text();
  // verifySignature is a placeholder – in production, use a Standard Webhooks library
  const { valid, event } = await verifySignature(
    payload,
    { id: webhookId, signature: webhookSignature, timestamp: webhookTimestamp },
    secret
  );
  if (!valid) return NextResponse.json({ error: 'Invalid signature' }, { status: 400 });

  switch (event.type) {
    case 'subscription.active':
      // mark subscription active in your DB
      break;
    case 'subscription.plan_changed':
      // refresh entitlements and reflect the new plan in your UI
      break;
    case 'subscription.on_hold':
      // notify user to update payment method
      break;
    case 'subscription.renewed':
      // extend access window
      break;
    case 'payment.succeeded':
      // reconcile payment for plan change
      break;
    case 'payment.failed':
      // log and alert
      break;
    default:
      // ignore unknown events
      break;
  }

  return NextResponse.json({ received: true });
}
Để xem chi tiết định dạng tải payload, tham khảo Tải trọng webhook đăng kýTải trọng webhook thanh toán.

Các Thực tiễn Tốt nhất

Thực hiện các lời khuyên sau để đảm bảo thay đổi kế hoạch đăng ký đáng tin cậy:

Chiến lược Thay đổi Kế hoạch

  • Kiểm tra kỹ lưỡng: Luôn kiểm tra các thay đổi kế hoạch trong chế độ kiểm tra trước khi triển khai vào sản xuất
  • Chọn chế độ tỷ lệ cẩn thận: Chọn chế độ tỷ lệ phù hợp với mô hình kinh doanh của bạn
  • Xử lý lỗi một cách linh hoạt: Triển khai xử lý lỗi đúng cách và logic thử lại
  • Giám sát tỷ lệ thành công: Theo dõi tỷ lệ thành công/thất bại của thay đổi kế hoạch và điều tra các vấn đề

Triển khai Webhook

  • Xác nhận chữ ký: Luôn xác thực chữ ký webhook để đảm bảo tính xác thực
  • Triển khai idempotency: Xử lý các sự kiện webhook trùng lặp một cách linh hoạt
  • Xử lý không đồng bộ: Không chặn phản hồi webhook với các hoạt động nặng
  • Ghi đè mọi thứ: Duy trì log chi tiết để gỡ lỗi và kiểm toán

Trải nghiệm Người dùng

  • Giao tiếp rõ ràng: Thông báo cho khách hàng về các thay đổi thanh toán và thời gian
  • Cung cấp xác nhận: Gửi email xác nhận cho những thay đổi kế hoạch thành công
  • Xử lý các trường hợp biên: Xem xét thời gian thử nghiệm, các tỷ lệ và thanh toán thất bại
  • Cập nhật giao diện ngay lập tức: Phản ánh các thay đổi kế hoạch trong giao diện ứng dụng của bạn

Các sự cố Phổ biến và Giải pháp

Giải quyết các vấn đề thường gặp trong quá trình thay đổi kế hoạch đăng ký:
Triệu chứng: Lời gọi API thành công nhưng gói đăng ký vẫn giữ theo kế hoạch cũNguyên nhân phổ biến:
  • Xử lý webhook thất bại hoặc bị chậm trễ
  • Trạng thái ứng dụng không được cập nhật sau khi nhận webhook
  • Vấn đề giao dịch cơ sở dữ liệu trong quá trình cập nhật trạng thái
Giải pháp:
  • Triển khai xử lý webhook mạnh mẽ với logic thử lại
  • Sử dụng các hoạt động idempotent để cập nhật trạng thái
  • Thêm giám sát để phát hiện và cảnh báo về những sự kiện webhook bị thiếu
  • Xác minh endpoint webhook có thể truy cập và phản hồi chính xác
Triệu chứng: Khách hàng hạ cấp nhưng không thấy số dư tín dụngNguyên nhân phổ biến:
  • Kỳ vọng chế độ tỷ lệ: hạ cấp tín dụng toàn bộ sự chênh lệch giá kế hoạch với difference_immediately, trong khi prorated_immediately tạo ra một tín dụng tỷ lệ dựa trên thời gian còn lại trong chu kỳ
  • Các tín dụng là riêng cho từng gói đăng ký và không chuyển giữa các gói đăng ký
  • Số dư tín dụng không hiển thị trong bảng điều khiển khách hàng
Giải pháp:
  • Sử dụng difference_immediately cho hạ cấp khi bạn muốn tín dụng tự động
  • Giải thích với khách hàng rằng tín dụng áp dụng cho các lần gia hạn tương lai của cùng một gói đăng ký
  • Triển khai cổng thông tin khách hàng để hiển thị số dư tín dụng
  • Kiểm tra bản xem trước hóa đơn tiếp theo để xem tín dụng đã áp dụng
Triệu chứng: Sự kiện webhook bị từ chối do chữ ký không hợp lệNguyên nhân phổ biến:
  • Khóa bí mật webhook sai
  • Yêu cầu raw body đã bị sửa đổi trước khi xác minh chữ ký
  • Sai thuật toán xác minh chữ ký
Giải pháp:
  • Xác thực rằng bạn đang sử dụng đúng DODO_WEBHOOK_SECRET từ bảng điều khiển
  • Đọc body yêu cầu raw trước bất kỳ trình xử lý trung gian phân tích JSON nào
  • Sử dụng thư viện xác minh webhook tiêu chuẩn cho nền tảng của bạn
  • Kiểm tra xác minh chữ ký webhook trong môi trường phát triển
Triệu chứng: API trả về lỗi 422 Unprocessable EntityNguyên nhân phổ biến:
  • ID đăng ký không hợp lệ hoặc ID sản phẩm
  • Gói đăng ký không ở trạng thái hoạt động
  • Thiếu các tham số yêu cầu
  • Sản phẩm không khả dụng cho thay đổi kế hoạch
Giải pháp:
  • Xác minh gói đăng ký tồn tại và đang hoạt động
  • Kiểm tra ID sản phẩm hợp lệ và có sẵn
  • Đảm bảo mọi tham số yêu cầu đã được cung cấp
  • Xem lại tài liệu API để biết các yêu cầu về tham số
Triệu chứng: Thay đổi kế hoạch được khởi tạo nhưng khoản phí ngay lập tức thất bạiNguyên nhân phổ biến:
  • Không đủ tiền trên phương thức thanh toán của khách hàng
  • Phương thức thanh toán hết hạn hoặc không hợp lệ
  • Ngân hàng từ chối giao dịch
  • Phát hiện gian lận đã chặn khoản phí
Giải pháp:
  • Xử lý sự kiện webhook payment.failed một cách thích hợp
  • Thông báo cho khách hàng để cập nhật phương thức thanh toán
  • Triển khai logic thử lại cho các lỗi tạm thời
  • Cân nhắc cho phép thay đổi kế hoạch với các khoản phí ngay lập tức không thành công
Triệu chứng: Thay đổi kế hoạch phí thất bại và gói đăng ký chuyển sang trạng thái on_holdĐiều gì xảy ra: Khi một thay đổi kế hoạch phí thất bại, gói đăng ký tự động được đặt vào trạng thái on_hold. Gói đăng ký sẽ không tự động gia hạn cho đến khi phương thức thanh toán được cập nhật.Giải pháp: Cập nhật phương thức thanh toán để kích hoạt gói đăng ký lạiĐể kích hoạt lại một gói đăng ký từ trạng thái on_hold sau khi thay đổi kế hoạch thất bại:
  1. Cập nhật phương thức thanh toán sử dụng API Cập nhật Phương thức Thanh toán
  2. Tạo khoản phí tự động: API tự động tạo một khoản phí cho các khoản phải trả còn lại
  3. Tạo hóa đơn: Một hóa đơn được tạo cho khoản phí
  4. Xử lý thanh toán: Thanh toán được xử lý bằng phương thức thanh toán mới
  5. Kích hoạt lại: Sau khi thanh toán thành công, gói đăng ký được kích hoạt lại về trạng thái active
// Reactivate subscription from on_hold after failed plan change
async function reactivateAfterFailedPlanChange(subscriptionId) {
  // Update payment method - automatically creates charge for remaining dues
  const response = await client.subscriptions.updatePaymentMethod(subscriptionId, {
    type: 'new',
    return_url: 'https://example.com/return'
  });
  
  if (response.payment_id) {
    console.log('Charge created for remaining dues:', response.payment_id);
    console.log('Payment link:', response.payment_link);
    
    // Redirect customer to payment_link to complete payment
    // Monitor webhooks for:
    // 1. payment.succeeded - charge succeeded
    // 2. subscription.active - subscription reactivated
  }
  
  return response;
}

// Or use existing payment method if available
async function reactivateWithExistingPaymentMethod(subscriptionId, paymentMethodId) {
  const response = await client.subscriptions.updatePaymentMethod(subscriptionId, {
    type: 'existing',
    payment_method_id: paymentMethodId
  });
  
  // Monitor webhooks for payment.succeeded and subscription.active
  return response;
}
Các sự kiện webhook cần giám sát:
  • subscription.on_hold: Gói đăng ký bị giữ lại (được nhận khi thay đổi kế hoạch phí thất bại)
  • payment.succeeded: Thanh toán cho các khoản nợ còn lại thành công (sau khi cập nhật phương thức thanh toán)
  • subscription.active: Gói đăng ký được kích hoạt lại sau khi thanh toán thành công
Thực tiễn tốt nhất:
  • Thông báo ngay cho khách hàng khi thay đổi kế hoạch phí thất bại
  • Cung cấp hướng dẫn rõ ràng về cách cập nhật phương thức thanh toán của họ
  • Giám sát sự kiện webhook để theo dõi trạng thái kích hoạt lại
  • Cân nhắc triển khai logic thử lại tự động cho các lỗi thanh toán tạm thời

Update Payment Method API Reference

Xem toàn bộ tài liệu API để cập nhật phương thức thanh toán và kích hoạt lại gói đăng ký.

Thử nghiệm Triển khai của Bạn

Thực hiện các bước sau để thử nghiệm kỹ lưỡng triển khai thay đổi kế hoạch đăng ký của bạn:
1

Set up test environment

  • Sử dụng khóa API thử nghiệm và sản phẩm thử nghiệm
  • Tạo gói đăng ký thử nghiệm với các loại kế hoạch khác nhau
  • Cấu hình endpoint webhook thử nghiệm
  • Thiết lập giám sát và ghi log
2

Test different proration modes

  • Kiểm tra prorated_immediately với các vị trí chu kỳ thanh toán khác nhau
  • Kiểm tra difference_immediately cho nâng cấp và hạ cấp
  • Kiểm tra full_immediately để đặt lại chu kỳ thanh toán
  • Kiểm tra do_not_bill cho các chuyển đổi kế hoạch không tính phí/không có tín dụng
  • Xác minh tính toán tín dụng là chính xác
3

Test webhook handling

  • Xác minh tất cả các sự kiện webhook liên quan được nhận
  • Kiểm tra xác minh chữ ký webhook
  • Xử lý sự kiện webhook trùng lặp một cách linh hoạt
  • Kiểm tra kịch bản xử lý thất bại webhook
4

Test error scenarios

  • Kiểm tra với ID đăng ký không hợp lệ
  • Kiểm tra với phương thức thanh toán hết hạn
  • Kiểm tra lỗi mạng và thời gian chờ
  • Kiểm tra với số tiền không đủ
5

Monitor in production

  • Thiết lập cảnh báo cho những thay đổi kế hoạch không thành công
  • Giám sát thời gian xử lý webhook
  • Theo dõi tỷ lệ thành công của thay đổi kế hoạch
  • Xem xét các vé hỗ trợ khách hàng về vấn đề thay đổi kế hoạch

Xử lý Lỗi

Xử lý các lỗi API phổ biến một cách linh hoạt trong triển khai của bạn:

Mã Trạng thái HTTP

Yêu cầu thay đổi kế hoạch được xử lý thành công. Gói đăng ký đang được cập nhật và xử lý thanh toán đã bắt đầu.
Các tham số yêu cầu không hợp lệ. Kiểm tra rằng tất cả các trường cần thiết đã được cung cấp và định dạng chính xác.
Khóa API không hợp lệ hoặc bị thiếu. Xác thực DODO_PAYMENTS_API_KEY của bạn là chính xác và có quyền hạn đúng.
ID gói đăng ký không tìm thấy hoặc không thuộc tài khoản của bạn.
Gói đăng ký không thể thay đổi (ví dụ: đã hủy, sản phẩm không có sẵn, v.v.).
Đã xảy ra lỗi máy chủ. Thử lại yêu cầu sau một thời gian ngắn.

Định dạng Phản hồi Lỗi

{
  "error": {
    "code": "subscription_not_found",
    "message": "The subscription with ID 'sub_123' was not found",
    "details": {
      "subscription_id": "sub_123"
    }
  }
}

Bước tiếp theo

Last modified on April 20, 2026