billing

POST /v1/subscriptions/{subscriptionId}/plan

Move a subscription onto a bigger or smaller plan.

所有 billing 端点

所有开发者文档

身份验证

请将 API 密钥作为 bearer 令牌发送。该密钥必须具有 billing.payment.manage 权限;缺少该权限的密钥将被拒绝并返回 403 状态码,而非 404。

此端点不需要组织 ID。您的密钥已用于识别其所属的组织,且响应范围也仅限于该组织。

免费试用

将尖括号中的内容替换为您自己的值,并将键占位符替换为您仪表板中的一个键。

curl -X POST https://api.zinndigital.com/v1/subscriptions/{subscriptionId}/plan \
  -H "Authorization: Bearer zdk_live_…" \
  -H "Content-Type: application/json" \
  -d '{ "plan_version_id": <Uuid> }'

已登录?您仪表板中的 API 控制台会自动填入您真实的组织 ID 和您自己的密钥,并针对实时 API 运行请求,以便您查看实际的响应。 在 API 控制台中打开此端点

详细信息

Charges the prorated difference for the remainder of the current period, moves every site the subscription entitles, and supersedes the subscription onto the new plan version — keeping the existing period window, so the customer's renewal date does not move and they are not handed a free extra period. ⛔ A plan change SUPERSEDES rather than mutates, because a subscription pins the exact version it was sold so that a later catalogue edit cannot rewrite what a live subscriber pays. subscription_id in the response is therefore a new id and a client holding the old one must replace it. ⭐⭐ slots_before and slots_after are read back from the subscription rows, not derived from the plan that was asked for. A plan change that answers 200 and silently does not take effect is the worst outcome on this path, and a status code cannot tell that apart from one that worked. Mid-period money. A larger plan is charged the difference now — the customer asked for more room today, and applying it at the next renewal is the buy-flow dead end in another costume. A smaller plan returns the unused difference as account credit, not a card refund: it is instant, it cannot be unavailable, it is idempotent, and it is spendable on their next order. ⛔ Account credit carries no VAT credit note; a proration credit spans the current period's invoice rather than reversing it. ⛔ expected_total_minor is a money guard, not a convenience. It is the figure the customer was actually shown, and the change is refused 422 when the freshly-computed quote differs. Every other refusal is 422 as well, with nothing charged and nothing changed: a plan on another product line, the plan they are already on, a plan that has been re-priced since the page loaded, account usage above what a smaller plan allows, a card decline. ⛔ The downgrade guard is account-wide and there is no override. The same check_downgrade the site endpoint and the staff tariff endpoint call: sites, mailboxes, mailbox storage and disk across the org's subtree. Staff have an audited override because they sometimes genuinely need to move an over-limit account; a customer overriding their own quota check is not an override, it is no check. Repeating an identical request is the recovery and it is safe. Every step is idempotent on a key anchored to the subscription being left, so an attempt that died part-way is finished rather than duplicated: one order, one charge, one credit, one supersede. 404 for a subscription in another tenant as well as for one that does not exist. 503 when the driver behind an entitled site cannot move a package between types at all — our gap, not theirs. Requires billing.payment.manage — it charges a card, so the key that gates paying is the key that gates committing to a payment.

参数

名称类型必填内容简介
subscriptionId (path)UuidThe subscription whose plan is being read or changed.

请求正文

名称类型必填内容简介
plan_version_idUuidThe priced plan version to move onto, exactly as getSitePlanChoices reported it.
expected_total_minorintegerA money guard, not a convenience. The figure the customer was actually shown, in minor units. The engine refuses 422 when its freshly-computed quote differs, so a price…
gatewayobjectThe payment rail the customer picked, from getPaymentOptions. Omit it (or send null) for "no preference", which follows the payment method the organization would be charged on…

响应

名称类型必填内容简介
plan_codestringThe plan this subscription is now on.
plan_namestringIts catalogue name, so a receipt can name it without a second lookup.
plan_version_idstringThe priced version the new subscription carries.
subscription_idstringThe new subscription. A plan change supersedes rather than mutates, because a subscription pins the exact version it was sold — so this id differs from the one in the request…
order_idstringThe order the charge was raised against, or "" when the change resulted in a credit and no order was minted.
charged_minorintegerWhat was actually taken, in minor units.
credited_minorintegerWhat was actually returned as account balance, in minor units.
package_typestringThe vendor package type, read back after the move. "" on our own fleet and "" when the subscription entitles no site — there is no package to move.
slots_beforeintegerThe site allowance the subscription held before the change.
slots_afterintegerThe site allowance it holds now.
slots_before_unlimitedbooleanThe old plan sold unlimited sites, so slots_before is null by design.
slots_after_unlimitedbooleanThe new plan sells unlimited sites, so slots_after is null by design.
quoteSitePlanQuoteThe quote the change was performed against.

此端点可能返回的错误

401 · 403 · 404 · 422 · 429 · 503