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

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.

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_codes
array
Mã giảm giá chồng lên nhau tùy chọn để áp dụng cho gói mới (tối đa 20, được áp dụng theo thứ tự mảng). Hành vi phụ thuộc vào những gì bạn chuyển:
  • Không cung cấp / null — các giảm giá hiện có với preserve_on_plan_change=true được bảo lưu nếu áp dụng cho sản phẩm mới.
  • [] (mảng trống) — xóa tất cả các giảm giá hiện có khỏi đăng ký.
  • ["CODE_A", "CODE_B", ...] — thay thế bất kỳ giảm giá hiện có nào bằng tập hợp chồng lên nhau này.
discount_code
string
không còn sử dụng
Không dùng — ưu tiên discount_codes cho các tích hợp mới. Trường này vẫn hoạt động cho sự tương thích ngược, nhưng không thể kết hợp với discount_codes trong cùng một yêu cầu.
effective_at
string
mặc định:"immediately"
Khi nào áp dụng thay đổi gói:
  • immediately (mặc định): Áp dụng thay đổi gói 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 gói hiện tại của họ cho đến khi kết thúc kỳ thanh toán.
Sử dụng next_billing_date cho việc hạ cấp để khách hàng giữ lại các lợi ích của gói hiện tại cho đến khi kỳ thanh toán kết thúc.
4

Handle Webhook Events

Thiết lập xử lý webhook để theo dõi kết quả thay đổi gói:
  • subscription.active: Thay đổi gói thành công, đăng ký được cập nhật
  • subscription.plan_changed: Kế hoạch đăng ký thay đổi (nâng cấp/hạ cấp/ cập nhật addon)
  • subscription.on_hold: Phí thay đổi gói thất bại, đăng ký tạm dừng
  • payment.succeeded: Phí ngay lập tức cho thay đổi gói thành công
  • payment.failed: Phí ngay lập tức thất bại
Luôn xác minh chữ ký webhook và thực hiện 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ục hồi tính năng dựa trên gói mới
  • Cập nhật bảng điều khiển khách hàng với chi tiết gói mới
  • Gửi email xác nhận về thay đổi gói
  • Ghi lại thay đổi thanh toán cho mục đích kiểm tra
6

Test and Monitor

Kiểm tra kỹ lưỡng việc triển khai của bạn:
  • Kiểm tra tất cả chế độ phân bổ 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 thay đổi gói
  • Thiết lập cảnh báo cho lỗi thay đổi gói
Việc triển khai thay đổi kế hoạch đăng ký của bạn đã sẵn sàng cho việc sử dụng sản phẩm.

Xem trước Thay đổi Gói

Trước khi cam kết thay đổi gói, sử dụng API Xem trước để hiển thị cho khách hàng chính xác số tiền họ sẽ phải trả:
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 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ẽ phải trả trước khi họ xác nhận thay đổi gói.

API Thay đổi Gói

Sử dụng API Thay đổi Gói để thay đổi sản phẩm, số lượng và hành vi phân bổ cho một đăng ký đang hoạt động.

Ví dụ khởi đầ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í ngay lập tức và/hoặc hóa đơn được tạo ra trong quá trình thay đổi gói. Luôn dựa vào sự kiện webhook (vd., payment.succeeded, subscription.plan_changed) để xác nhận kết quả.
Nếu phí ngay lập tức thất bại, đăng ký có thể chuyển đến subscription.on_hold cho đến khi thanh toán thành công.

Quản lý Addons

Khi thay đổi kế hoạch đăng ký, bạn cũng có thể sửa đổi 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 phân bổ và sẽ được tính phí theo chế độ phân bổ đã chọn.

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

Bạn có thể áp dụng một hoặc nhiều mã giảm giá chồng lên nhau khi thay đổi đăng ký (tối đa 20, áp dụng theo thứ tự mảng). Điều này hữu ích cho việc cung cấp giá khuyến mại khi nâng cấp hoặc di cư.
// Apply stacked discount codes during plan change
await client.subscriptions.changePlan('sub_123', {
  product_id: 'prod_pro',
  quantity: 1,
  proration_billing_mode: 'prorated_immediately',
  discount_codes: ['UPGRADE20']
});

Hành vi giảm giá khi thay đổi gói

discount_codes valueHành vi
Không cung cấp / nullCác giảm giá hiện có với preserve_on_plan_change=true được tự động bảo lưu nếu áp dụng cho sản phẩm mới.
[] (mảng trống)Tất cả các giảm giá hiện có sẽ bị xóa khỏi đăng ký.
["CODE_A", "CODE_B", ...]Thay thế bất kỳ giảm giá hiện có nào bằng tập hợp chồng lên nhau này, được xác nhận và áp dụng theo thứ tự mảng.
Trường đơn discount_code trên đầu cuối này là không dùng nhưng vẫn hoạt động để tương thích ngược — các tích hợp hiện tại không cần thay đổi ngay lập tức. Nó không thể kết hợp với discount_codes trong cùng một yêu cầu. Di chuyển sang dạng mảng khi thuận tiện.
Sử dụng API Xem trước Thay đổi Gói với discount_codes để hiển thị cho khách hàng chính xác số tiền họ sẽ tiết kiệm trước khi xác nhận thay đổi gói.

Các chế độ phân bổ

Chọn cách tính phí cho khách hàng khi thay đổi gói:

prorated_immediately

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

full_immediately

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

difference_immediately

  • Nâng cấp: tính ngay sự khác biệt về giá giữa các gói cũ và mới
  • Hạ cấp: thêm giá trị còn lại làm tín dụng nội bộ cho đăng ký và tự động áp dụng khi gia hạn

do_not_bill

  • Không tính phí hoặc tính toán tín dụng
  • Khách hàng chuyển sang gói mới ngay lập tức mà không có bất kỳ điều chỉnh thanh toán nào
  • Chu kỳ thanh toán không thay đổi
  • Tốt nhất cho chuyển đổi ân huệ, chuyển đổi gói miễn phí hoặc hấp thụ sự khác biệt chi phí
Tính năngprorated_immediatelydifference_immediatelyfull_immediatelydo_not_bill
Phí nâng cấpKhác biệt theo thời gian đã sử dụngSự khác biệt toàn bộ giá giữa các góiToàn bộ giá của gói mớiKhông phí
Tín dụng hạ cấpTín dụng theo thời gian còn lạiSự khác biệt toàn bộ giá như tín dụngKhông tín dụngKhông tín dụng
Chu kỳ thanh toánKhông thay đổiKhông thay đổiKhởi động lại từ 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í toàn bộKết thúc thử nghiệm, không tính phí
Tốt nhất choTính phí công bằng theo thời gianToán học nâng cấp/hạ cấp đơn giảnKhởi động lại các chu kỳ thanh toánChuyển đổi miễn phí hoặc ân huệ
Độ phức tạpTrung bình (tính toán ngày)Thấp (trừ đơn giản)Thấp (tính phí toàn bộ)Không có

Các kịch bản ví dụ

Sử dụng những con số chuẩn này một cách nhất quán:
  • Gói hiện tại: Cơ bản 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): Khởi đầu với $20/tháng
  • Chu kỳ thanh toán: 30 ngày, bắt đầu từ 1 tháng 1
  • Thay đổi kế hoạch xảy ra vào 16 tháng 1 (còn 15 ngày, đã sử dụng 15 ngày)
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ý việc thanh toán

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

Xử lý lỗi Thanh toán

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

Chế độ Lỗi Thanh toán

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

Khi nào sử dụng mỗi chế độ

Kịch bảnChế độ được 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 cung cấp quyền truy cập
Tăng số lượng (nhiều chỗ)prevent_changeNgăn chặn việc sử dụng mà không có thanh toán
Hạ cấp kế hoạchapply_changeKhách hàng đang giảm tiêu dùng
Khách hàng doanh nghiệp đáng tin cậyapply_changeRủi ro thấp không thanh toán
Chuyển đổi thử nghiệm sang thanh toánprevent_changeKhoảnh khắc thanh toán quan trọng

Xử lý webhooks

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

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

  • subscription.active: đă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: phí thất bại, đăng ký tạm dừng
  • subscription.renewed: gia hạn thành công
  • payment.succeeded: thanh toán cho thay đổi gói 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ị điều khiển logic kinh doanh từ các sự kiện đăng ký và sử dụng các sự kiện thanh toán để xác nhận và đối chiếu.

Xác minh 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 cấu trúc tải trọng, xem Tải trọng webhook Đăng kýTải trọng webhook Thanh toán.

Thực hành Tốt nhất

Tuân thủ các khuyến nghị này để thay đổi gói đăng ký đáng tin cậy:

Chiến lược Thay đổi Gói

  • Kiểm tra kỹ lưỡng: Luôn kiểm tra thay đổi gói trong chế độ thử nghiệm trước sản xuất
  • Chọn phân bổ cẩn thận: Chọn chế độ phân bổ phù hợp với mô hình kinh doanh của bạn
  • Xử lý lỗi một cách tinh tế: Triển khai xử lý lỗi và logic thử lại thích hợp
  • 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 gói và điều tra các vấn đề

Triển khai Webhook

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

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

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

Vấn đề Thông thường và Giải pháp

Giải quyết các vấn đề thường gặp khi thay đổi kế hoạch đăng ký:
Triệu chứng: Cuộc gọi API thành công nhưng đăng ký vẫn ở gói cũNguyên nhân thường gặp:
  • Xử lý webhook thất bại hoặc bị trì hoãn
  • 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 khi 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ề sự kiện webhook bị bỏ lỡ
  • Xác minh endpoint webhook có thể truy cập và phản hồi đúng cách
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 thường gặp:
  • Kỳ vọng chế độ phân bổ: hạ cấp tín dụng sự khác biệt giá toàn bộ với difference_immediately, trong khi prorated_immediately tạo tín dụng phân bổ dựa trên thời gian còn lại trong chu kỳ
  • Tín dụng cụ thể cho đăng ký và không chuyển nhượng giữa các đă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 các hạ cấp khi bạn muốn tự động tín dụng
  • Giải thích cho khách hàng rằng các khoản tín dụng áp dụng cho các gia hạn trong tương lai của cùng một đă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 xem trước hóa đơn tiếp theo để thấy các 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 thường gặp:
  • Key bí mật webhook không chính xác
  • Dữ liệu yêu cầu thô bị thay đổ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 minh bạn đang sử dụng đúng DODO_WEBHOOK_SECRET từ bảng điều khiển
  • Đọc dữ liệu yêu cầu thô trước bất kỳ middleware phân giải 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 Đối tượng không thể xử lýNguyên nhân thường gặp:
  • ID đăng ký hoặc ID sản phẩm không hợp lệ
  • Đăng ký không ở trạng thái hoạt động
  • Thiếu các tham số cần thiết
  • Sản phẩm không có sẵn để thay đổi kế hoạch
Giải pháp:
  • Xác minh đă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 tất cả tham số cần thiết đượ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: Khởi tạo thay đổi gói nhưng phí ngay lập tức thất bạiNguyên nhân thường gặp:
  • 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ý các sự kiện webhook payment.failed một cách thích hợp
  • Thông báo 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
  • Xem xét cho phép thay đổi gói với các khoản phí ngay lập tức thất bại
Triệu chứng: Phí thay đổi gói thất bại và đăng ký chuyển sang trạng thái on_holdĐiều gì xảy ra: Khi phí thay đổi gói thất bại, đăng ký sẽ tự động được đặt ở trạng thái on_hold. Đăng ký sẽ không được gia hạn tự động 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 để tái kích hoạt đăng kýĐể tái kích hoạt đăng ký từ trạng thái on_hold sau khi thay đổi gói 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 khoản phí cho các khoản còn nợ
  3. Tạo hóa đơn: 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. Tái kích hoạt: Khi thanh toán thành công, đăng ký được tái kích hoạt 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;
}
Sự kiện webhook để giám sát:
  • subscription.on_hold: Đăng ký bị tạm ngưng (nhận được khi phí thay đổi gói thất bại)
  • payment.succeeded: Thanh toán cho các khoản còn lại thành công (sau khi cập nhật phương thức thanh toán)
  • subscription.active: Đăng ký được tái kích hoạt sau khi thanh toán thành công
Thực hành tốt nhất: 2
  • Thông báo ngay lập tức cho khách hàng khi phí thay đổi gói 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
  • Giám sát sự kiện webhook để theo dõi trạng thái tái kích hoạt
  • Xem xét 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à tái kích hoạt đăng ký.

Kiểm tra Triển khai của Bạn

Thực hiện các bước sau để kiểm tra kỹ lưỡng việc 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 kiểm tra và sản phẩm kiểm tra
  • Tạo đăng ký kiểm tra với các loại gói khác nhau
  • Cấu hình endpoint webhook kiểm tra
  • Thiết lập giám sát và ghi nhật ký
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 để khởi động lại chu kỳ thanh toán
  • Kiểm tra do_not_bill cho chuyển đổi gói không tính phí/không 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ý các sự kiện webhook trùng lặp một cách tinh tế
  • Kiểm tra các kịch bản thất bại xử lý webhook
4

Test error scenarios

  • Kiểm tra với các 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 các lỗi mạng và hết thời gian
  • Kiểm tra với tiền không đủ
5

Monitor in production

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

Xử lý lỗi

Xử lý các lỗi API phổ biến một cách tinh 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. Đăng ký đang được cập nhật và xử lý thanh toán đã bắt đầu.
Các thông 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 đúng.
API key không hợp lệ hoặc thiếu. Xác minh DODO_PAYMENTS_API_KEY của bạn là chính xác và có quyền phù hợp.
ID đăng ký không được tìm thấy hoặc không thuộc về tài khoản của bạn.
Đăng ký không thể thay đổi (ví dụ: đã hủy, sản phẩm không có sẵn, v.v.).
Có lỗi máy chủ xảy ra. 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

Next steps

Last modified on May 22, 2026