> ## 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.

# Thay đổi gói

> Chỉnh sửa gói của một đăng ký hiện có, cho phép cả việc nâng cấp và hạ cấp xuống các mức giá khác nhau.<br/><br/>Lưu ý&colon; Điều này sẽ sử dụng thông tin thanh toán hiện có của khách hàng để nâng cấp/hạ cấp gói.

## Thay đổi kế hoạch đã lên lịch

Sử dụng tham số `effective_at` để kiểm soát khi nào thay đổi kế hoạch có hiệu lực:

| Giá trị             | Hành vi                                                                                                                                       |
| ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `immediately`       | Áp dụng thay đổi kế hoạch ngay lập tức. Đây là mặc định.                                                                                      |
| `next_billing_date` | Lên lịch thay đổi vào ngày lập hóa đơn tiếp theo. Khách hàng giữ quyền truy cập vào kế hoạch hiện tại cho đến khi kết thúc chu kỳ thanh toán. |

<Info>
  Thay đổi kế hoạch đã lên lịch là lý tưởng cho việc hạ cấp — khách hàng giữ quyền lợi kế hoạch hiện tại cho đến cuối chu kỳ thanh toán, sau đó tự động chuyển sang kế hoạch mới.
</Info>

Để hủy thay đổi kế hoạch đã lên lịch trước khi có hiệu lực, sử dụng endpoint [Cancel Scheduled Plan Change](/api-reference/subscriptions/cancel-change-plan).

## Xử lý lỗi thanh toán

Sử dụng tham số `on_payment_failure` để kiểm soát điều gì xảy ra khi thanh toán thay đổi kế hoạch thất bại:

| Giá trị          | Hành vi                                                                                                     |
| ---------------- | ----------------------------------------------------------------------------------------------------------- |
| `prevent_change` | Giữ đăng ký trên kế hoạch hiện tại cho đến khi thanh toán thành công. Thay đổi kế hoạch vẫn đang chờ xử lý. |
| `apply_change`   | Áp dụng thay đổi kế hoạch ngay lập tức bất kể kết quả thanh toán. Đây là mặc định.                          |

<Info>
  Nếu `on_payment_failure` không được chỉ định, hành vi sẽ mặc định theo cấu hình cấp doanh nghiệp của bạn trong bảng điều khiển.
</Info>

## Mã giảm giá

Bạn có thể áp dụng một hoặc nhiều **mã giảm giá xếp chồng** khi thay đổi gói bằng cách truyền mảng `discount_codes` (tối đa 20 mục, áp dụng theo thứ tự của mảng). Trường đơn `discount_code` đã lỗi thời nhưng vẫn hoạt động cho các tích hợp hiện có; không thể kết hợp với `discount_codes` trong cùng một yêu cầu.

| Giá trị `discount_codes`              | Hành vi                                                                                                    |
| ------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
| Không được cung cấp (`null` / bỏ qua) | Các giảm giá hiện có với `preserve_on_plan_change=true` được giữ nếu áp dụng cho sản phẩm mới.             |
| `[]` (mảng rỗng)                      | **Tất cả** các giảm giá hiện có sẽ bị loại bỏ khỏi đăng ký.                                                |
| `["CODE_A", "CODE_B", ...]`           | Thay thế bất kỳ giảm giá hiện có nào bằng bộ xếp chồng này, được xác thực và áp dụng theo thứ tự của mảng. |

<Tip>
  Sử dụng mã giảm giá trong quá trình thay đổi gói để cung cấp giá khuyến mãi khi nâng cấp, hoặc truyền mã khi di chuyển khách hàng sang cấp gói mới.
</Tip>

<Tip>
  Sử dụng `prevent_change` cho những nâng cấp quan trọng mà bạn muốn đảm bảo thanh toán trước khi cấp quyền truy cập vào các tính năng cao cấp.
</Tip>


## OpenAPI

````yaml post /subscriptions/{subscription_id}/change-plan
openapi: 3.1.0
info:
  title: public
  description: ''
  license:
    name: Apache 2.0
    url: https://www.apache.org/licenses/LICENSE-2.0
  version: 1.106.2
servers:
  - url: https://test.dodopayments.com/
    description: Test Mode Server Host
  - url: https://live.dodopayments.com/
    description: Live Mode Server Host
security: []
tags:
  - name: Products
  - name: Payments
  - name: Subscriptions
  - name: Addons
  - name: Customers
  - name: Refunds
  - name: Disputes
  - name: Events
  - name: License Keys
  - name: Entitlements
  - name: Licenses
  - name: Discounts
  - name: Meters
  - name: Credit Entitlements
  - name: Credit Entitlement Balances
  - name: Outgoing Webhooks
  - name: Checkout
  - name: Webhook Events
  - name: Payment Connector Webhooks
paths:
  /subscriptions/{subscription_id}/change-plan:
    post:
      tags:
        - Subscriptions
      operationId: update_subscription_plan_handler
      parameters:
        - name: subscription_id
          in: path
          description: Subscription Id
          required: true
          schema:
            type: string
          example: sub_Iuaq622bbmmfOGrVTqdXv
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateSubscriptionPlanReq'
        required: true
      responses:
        '200':
          description: >-
            Subscription plan changed. If on_payment_failure=prevent_change, the
            plan change is pending until payment succeeds.
        '409':
          description: >-
            A pending plan change already exists for this subscription
            (PendingPlanChangeExists)
        '422':
          description: Subscription is inactive or on-demand, plan change not supported
        '500':
          description: Something went wrong :(
      security:
        - API_KEY: []
      x-codeSamples:
        - lang: JavaScript
          source: |-
            import DodoPayments from 'dodopayments';

            const client = new DodoPayments({
              bearerToken: process.env['DODO_PAYMENTS_API_KEY'], // This is the default and can be omitted
            });

            await client.subscriptions.changePlan('sub_Iuaq622bbmmfOGrVTqdXv', {
              product_id: 'product_id',
              proration_billing_mode: 'prorated_immediately',
              quantity: 0,
            });
        - lang: Python
          source: |-
            import os
            from dodopayments import DodoPayments

            client = DodoPayments(
                bearer_token=os.environ.get("DODO_PAYMENTS_API_KEY"),  # This is the default and can be omitted
            )
            client.subscriptions.change_plan(
                subscription_id="sub_Iuaq622bbmmfOGrVTqdXv",
                product_id="product_id",
                proration_billing_mode="prorated_immediately",
                quantity=0,
            )
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\n\t\"github.com/dodopayments/dodopayments-go\"\n\t\"github.com/dodopayments/dodopayments-go/option\"\n)\n\nfunc main() {\n\tclient := dodopayments.NewClient(\n\t\toption.WithBearerToken(\"My Bearer Token\"),\n\t)\n\terr := client.Subscriptions.ChangePlan(\n\t\tcontext.TODO(),\n\t\t\"sub_Iuaq622bbmmfOGrVTqdXv\",\n\t\tdodopayments.SubscriptionChangePlanParams{\n\t\t\tUpdateSubscriptionPlanReq: dodopayments.UpdateSubscriptionPlanReqParam{\n\t\t\t\tProductID:            dodopayments.F(\"product_id\"),\n\t\t\t\tProrationBillingMode: dodopayments.F(dodopayments.UpdateSubscriptionPlanReqProrationBillingModeProratedImmediately),\n\t\t\t\tQuantity:             dodopayments.F(int64(0)),\n\t\t\t},\n\t\t},\n\t)\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n}\n"
        - lang: Java
          source: >-
            package com.dodopayments.api.example;


            import com.dodopayments.api.client.DodoPaymentsClient;

            import com.dodopayments.api.client.okhttp.DodoPaymentsOkHttpClient;

            import
            com.dodopayments.api.models.subscriptions.SubscriptionChangePlanParams;

            import
            com.dodopayments.api.models.subscriptions.UpdateSubscriptionPlanReq;


            public final class Main {
                private Main() {}

                public static void main(String[] args) {
                    DodoPaymentsClient client = DodoPaymentsOkHttpClient.fromEnv();

                    SubscriptionChangePlanParams params = SubscriptionChangePlanParams.builder()
                        .subscriptionId("sub_Iuaq622bbmmfOGrVTqdXv")
                        .updateSubscriptionPlanReq(UpdateSubscriptionPlanReq.builder()
                            .productId("product_id")
                            .prorationBillingMode(UpdateSubscriptionPlanReq.ProrationBillingMode.PRORATED_IMMEDIATELY)
                            .quantity(0)
                            .build())
                        .build();
                    client.subscriptions().changePlan(params);
                }
            }
        - lang: Kotlin
          source: >-
            package com.dodopayments.api.example


            import com.dodopayments.api.client.DodoPaymentsClient

            import com.dodopayments.api.client.okhttp.DodoPaymentsOkHttpClient

            import
            com.dodopayments.api.models.subscriptions.SubscriptionChangePlanParams

            import
            com.dodopayments.api.models.subscriptions.UpdateSubscriptionPlanReq


            fun main() {
                val client: DodoPaymentsClient = DodoPaymentsOkHttpClient.fromEnv()

                val params: SubscriptionChangePlanParams = SubscriptionChangePlanParams.builder()
                    .subscriptionId("sub_Iuaq622bbmmfOGrVTqdXv")
                    .updateSubscriptionPlanReq(UpdateSubscriptionPlanReq.builder()
                        .productId("product_id")
                        .prorationBillingMode(UpdateSubscriptionPlanReq.ProrationBillingMode.PRORATED_IMMEDIATELY)
                        .quantity(0)
                        .build())
                    .build()
                client.subscriptions().changePlan(params)
            }
        - lang: Ruby
          source: |-
            require "dodopayments"

            dodo_payments = Dodopayments::Client.new(
              bearer_token: "My Bearer Token",
              environment: "test_mode" # defaults to "live_mode"
            )

            result = dodo_payments.subscriptions.change_plan(
              "sub_Iuaq622bbmmfOGrVTqdXv",
              product_id: "product_id",
              proration_billing_mode: :prorated_immediately,
              quantity: 0
            )

            puts(result)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Dodopayments\Client;
            use Dodopayments\Core\Exceptions\APIException;

            $client = new Client(
              bearerToken: getenv('DODO_PAYMENTS_API_KEY') ?: 'My Bearer Token',
              environment: 'test_mode',
            );

            try {
              $result = $client->subscriptions->changePlan(
                'sub_Iuaq622bbmmfOGrVTqdXv',
                productID: 'product_id',
                prorationBillingMode: 'prorated_immediately',
                quantity: 0,
                adaptiveCurrencyFeesInclusive: true,
                addons: [['addonID' => 'addon_id', 'quantity' => 0]],
                discountCode: 'discount_code',
                discountCodes: ['string'],
                effectiveAt: 'immediately',
                metadata: ['foo' => 'string'],
                onPaymentFailure: 'prevent_change',
              );

              var_dump($result);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: C#
          source: |-
            using DodoPayments.Client;
            using DodoPayments.Client.Models.Subscriptions;

            DodoPaymentsClient client = new();

            SubscriptionChangePlanParams parameters = new()
            {
                SubscriptionID = "sub_Iuaq622bbmmfOGrVTqdXv",
                ProductID = "product_id",
                ProrationBillingMode = ProrationBillingMode.ProratedImmediately,
                Quantity = 0,
            };

            await client.Subscriptions.ChangePlan(parameters);
        - lang: Rust
          source: |-
            use dodopayments::Client;

            #[tokio::main]
            async fn main() -> dodopayments::Result<()> {
                let client = Client::from_env()?;
                let subscription_id = "subscription_id";
                let result = client
                    .subscriptions()
                    .change_plan()
                    .subscription_id(subscription_id)
                    .body(dodopayments::models::SubscriptionsChangePlanParams {
                            product_id: Some("product_id".to_string()),
                            proration_billing_mode: Some("prorated_immediately".to_string()),
                            quantity: Some(0),
                            ..Default::default()
                        })
                    .await?;
                println!("{result:?}");
                Ok(())
            }
components:
  schemas:
    UpdateSubscriptionPlanReq:
      type: object
      required:
        - product_id
        - quantity
        - proration_billing_mode
      properties:
        adaptive_currency_fees_inclusive:
          type:
            - boolean
            - 'null'
          description: >-
            Whether adaptive currency fees should be included in the price
            (true) or added on top (false).

            If not specified, uses the subscription's stored setting.
        addons:
          type:
            - array
            - 'null'
          items:
            $ref: '#/components/schemas/AttachAddonReq'
          description: |-
            Addons for the new plan.
            Note : Leaving this empty would remove any existing addons
        discount_code:
          type:
            - string
            - 'null'
          description: >-
            DEPRECATED: Use discount_codes instead. Cannot be used together with
            discount_codes.
          deprecated: true
          x-stainless-deprecation-message: Use `discount_id` instead.
        discount_codes:
          type:
            - array
            - 'null'
          items:
            type: string
          description: >-
            Stacked discount codes to apply to the new plan. Max 20.

            Cannot be used together with discount_code.

            If provided, replaces any existing discount codes.

            Empty array removes all discounts.

            If not provided (None), existing discounts with
            preserve_on_plan_change=true are preserved.
        effective_at:
          $ref: '#/components/schemas/EffectiveAt'
          description: |-
            When to apply the plan change.
            - `immediately` (default): Apply the plan change right away
            - `next_billing_date`: Schedule the change for the next billing date
        metadata:
          oneOf:
            - type: 'null'
            - $ref: '#/components/schemas/Metadata'
              description: >-
                Metadata for the payment. If not passed, the metadata of the
                subscription will be taken
        on_payment_failure:
          oneOf:
            - type: 'null'
            - $ref: '#/components/schemas/OnPaymentFailure'
              description: >-
                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.
        product_id:
          type: string
          description: Unique identifier of the product to subscribe to
        proration_billing_mode:
          $ref: '#/components/schemas/ProrationBillingMode'
          description: Proration Billing Mode
        quantity:
          type: integer
          format: int32
          description: Number of units to subscribe for. Must be at least 1.
          minimum: 0
    AttachAddonReq:
      type: object
      title: Attach Addon Request
      required:
        - addon_id
        - quantity
      properties:
        addon_id:
          type: string
        quantity:
          type: integer
          format: int32
          description: Number of units of this addon.
          minimum: 0
    EffectiveAt:
      type: string
      description: When to apply a subscription plan change.
      enum:
        - immediately
        - next_billing_date
    Metadata:
      type: object
      title: Metadata
      description: >-
        Arbitrary key-value metadata. Values can be string, integer, number, or
        boolean.
      additionalProperties:
        oneOf:
          - type: string
            title: String
          - type: integer
            title: Integer
            format: int64
          - type: number
            title: Number
            format: double
          - type: boolean
            title: Boolean
        title: Metadata Value
        description: Metadata value can be a string, integer, number, or boolean
    OnPaymentFailure:
      type: string
      description: >-
        Specifies how to handle subscription plan changes when payment fails.


        This enum controls whether the subscription should be updated
        immediately

        or only after payment succeeds.
      enum:
        - prevent_change
        - apply_change
    ProrationBillingMode:
      type: string
      title: Proration Billing Mode
      enum:
        - prorated_immediately
        - full_immediately
        - difference_immediately
        - do_not_bill
  securitySchemes:
    API_KEY:
      type: http
      scheme: bearer

````