Sem efeito: diz o que `POST /change-tier` faria com o mesmo corpo. `:id` é a assinatura do plano, do próprio membro, na comunidade do X-CommunityId; de outra pessoa ou de outra comunidade responde 404. As recusas são as mesmas de `change-tier`.
`changeType`: `UPGRADE` (opção mais cara na mesma periodicidade), `DOWNGRADE`, `SAME_PRICE` ou `INTERVAL_CHANGE` (mensal ↔ anual). `mode`: `IMMEDIATE` só para `UPGRADE` com `immediate` ausente ou `true` e sem adicional que a opção nova deixe de oferecer; o resto é `SCHEDULED`, para `effectiveDate` (o fim do período pago), e `scheduledReason` diz por quê (`DOWNGRADE`, `SAME_PRICE`, `INTERVAL_CHANGE`, `ADD_ONS_END` ou `REQUESTED`).
No `IMMEDIATE`: `prorataAmount` é a diferença de preço proporcional aos `remainingDays` de `periodDays`; com menos de 3 dias ou menos de R$ 10, `floorApplied = true` e `amountDueNow = 0` (troca na hora, sem cobrança). `amountToCharge` soma a taxa da plataforma quando o comprador a paga (`feePayer = BUYER`). A cobrança é no cartão salvo (`cardRequired`, `hasSavedCard`), em até `maxInstallments` parcelas (meses restantes no anual; 1 no mensal).
Sempre: `newPrice` (preço por período da opção nova), `nextChargeDate` e `nextChargeTotal` (a primeira cobrança no preço novo, com os adicionais que continuam); `addOns` (cada adicional cobrado com o plano: `outcome = KEPT` continua, no valor da periodicidade nova, `newAmount`; `ENDS` termina com a troca); `contract` (`commitmentEndsAt` depois da troca; `carriedOver = true` quando o contrato atual continua valendo); `couponEnds` (o cupom da assinatura deixa de valer na troca); `replacesScheduledChange` (a troca agendada que esta substituiria, ou `null`).
**Adicionais na troca:** `addOnProductIds` (opcional) são adicionais que entram com a troca. A regra é a do checkout: cada um precisa estar na lista do plano de destino, ser assinatura mensal à venda, aparecer uma vez e não estar assinado ainda; senão 400 `param = addOnProductIds`. Em `addOns`, além de `KEPT` e `ENDS`, cada um aparece com `outcome = JOINS` (`newAmount` por período da opção nova, `joinsAt` e `amountDueNow`). No `IMMEDIATE`, o pedido é um só: `lines` traz a linha do plano (`kind = PLAN`, a diferença proporcional) e uma por adicional (`kind = ADD_ON`, o valor do período proporcional aos mesmos dias), cada uma com o seu piso (`floorApplied`) e `feePayer`; a taxa fixa da plataforma entra uma vez, na primeira linha cobrada. `prorataAmount`, `amountDueNow` e `amountToCharge` são os totais do pedido. Na troca que vale na renovação, os adicionais entram junto, nessa renovação, sem cobrança hoje (`lines = []`); um plano que renova por PIX ou boleto só aceita adicionais numa troca cobrada agora (400 `monetization.subscription.changeTierAddOnsNeedCard`). `nextChargeTotal` já soma os adicionais que entram.
**Cartão:** `needsCard = true` quando a troca cobra agora e não há cartão salvo válido: a tela pede o cartão e manda `cardToken` em `POST /change-tier`.
**Meios de pagamento:** `allowedMethods` traz os meios desse pedido pela regra do checkout (os meios aceitos pela opção de destino): `[{ method, totalAmount, cashAmount, installmentOptions? }]`, com `method` em `CREDIT_CARD`, `PIX` ou `BOLETO`. O total à vista é o mesmo em todos (a taxa da plataforma independe do meio); no cartão, `installmentOptions` (`[{ installments, percent, totalAmount, installmentAmount }]`) como no `calculate-price`, até `maxInstallments`. O boleto só aparece quando faltam mais de 4 dias para a renovação (3 de vencimento + 1 de compensação). Sem cobrança agora (agendada ou abaixo do piso), `allowedMethods = []`. `pendingChange`: a troca que espera o PIX ou o boleto (`purchaseId`, `orderId`, `paymentMethod`, `newProductId`, `addOnProductIds`, `amount`, `currency`, `pixCode`, `qrCode`, `boletoUrl`, `boletoBarcode`, `expiresAt`, `createdAt`), ou `null`; enquanto ela existe, `POST /change-tier` responde 409. Uma opção de plano só de link (`link_only`) nunca é destino (400 `param = newProductId`). **Empresa:** o admin da empresa com vínculo vivo age como o assinante na assinatura da empresa (`companyId` na resposta); funcionário da empresa que não é admin recebe 403 (`monetization.company.adminOnly`); quem não tem relação com ela, 404.