Simular a troca de plano
curl --request POST \
--url https://api.sandbox.z2pay.com/v1/subscriptions/{id}/change-plan/preview \
--header 'Content-Type: application/json' \
--header 'x-api-key: <api-key>' \
--data '
{
"planId": "<string>",
"items": [
{
"priceVersionId": "<string>",
"quantity": 1
}
],
"itemOverrides": {},
"prorationBehavior": "create_prorations"
}
'const options = {
method: 'POST',
headers: {'x-api-key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
planId: '<string>',
items: [{priceVersionId: '<string>', quantity: 1}],
itemOverrides: {},
prorationBehavior: 'create_prorations'
})
};
fetch('https://api.sandbox.z2pay.com/v1/subscriptions/{id}/change-plan/preview', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.sandbox.z2pay.com/v1/subscriptions/{id}/change-plan/preview"
payload = {
"planId": "<string>",
"items": [
{
"priceVersionId": "<string>",
"quantity": 1
}
],
"itemOverrides": {},
"prorationBehavior": "create_prorations"
}
headers = {
"x-api-key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text){
"error": {
"code": "VALIDATION_ERROR",
"message": "Validation failed",
"issues": [
{
"path": "status",
"message": "Status inválido. Valores aceitos: pending, waiting_payment, paid, refused, canceled, refunded"
},
{
"path": "startDate",
"message": "Data deve ser ISO 8601 com timezone (ex.: 2026-06-24T00:00:00Z)"
}
]
}
}{
"error": {
"code": "UNAUTHORIZED",
"message": "Invalid API key"
}
}{
"error": {
"code": "NOT_FOUND",
"message": "Resource not found"
}
}{
"error": {
"code": "CONFLICT",
"message": "No refundable payment found"
}
}Troca de plano
Simular a troca de plano
Roda a conta de uma troca de plano sem executá-la, devolvendo o valor e as recusas.
POST
/
subscriptions
/
{id}
/
change-plan
/
preview
Simular a troca de plano
curl --request POST \
--url https://api.sandbox.z2pay.com/v1/subscriptions/{id}/change-plan/preview \
--header 'Content-Type: application/json' \
--header 'x-api-key: <api-key>' \
--data '
{
"planId": "<string>",
"items": [
{
"priceVersionId": "<string>",
"quantity": 1
}
],
"itemOverrides": {},
"prorationBehavior": "create_prorations"
}
'const options = {
method: 'POST',
headers: {'x-api-key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
planId: '<string>',
items: [{priceVersionId: '<string>', quantity: 1}],
itemOverrides: {},
prorationBehavior: 'create_prorations'
})
};
fetch('https://api.sandbox.z2pay.com/v1/subscriptions/{id}/change-plan/preview', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.sandbox.z2pay.com/v1/subscriptions/{id}/change-plan/preview"
payload = {
"planId": "<string>",
"items": [
{
"priceVersionId": "<string>",
"quantity": 1
}
],
"itemOverrides": {},
"prorationBehavior": "create_prorations"
}
headers = {
"x-api-key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text){
"error": {
"code": "VALIDATION_ERROR",
"message": "Validation failed",
"issues": [
{
"path": "status",
"message": "Status inválido. Valores aceitos: pending, waiting_payment, paid, refused, canceled, refunded"
},
{
"path": "startDate",
"message": "Data deve ser ISO 8601 com timezone (ex.: 2026-06-24T00:00:00Z)"
}
]
}
}{
"error": {
"code": "UNAUTHORIZED",
"message": "Invalid API key"
}
}{
"error": {
"code": "NOT_FOUND",
"message": "Resource not found"
}
}{
"error": {
"code": "CONFLICT",
"message": "No refundable payment found"
}
}POST /subscriptions/:id/change-plan/preview
Faz parte do recurso Assinaturas — o conceito e os dez estados estão lá.
Roda a conta de trocar o plano sem efeito nenhum: nada é cobrado, agendado ou alterado. O corpo é idêntico ao da troca real, e o valor devolvido é o que a troca cobraria.
O que volta
netAmount é o que sairia de fatura agora, em centavos. Numa troca que seria agendada para a virada, ele é zero.
lines traz as duas pernas do rateio separadas: o crédito pelo que resta do plano atual e o débito do novo. Cada valor é positivo — o sinal está em kind, que vale credit ou debit. Somando com o sinal, chega-se ao netAmount.
effectiveAt e effectiveDate dizem quando a troca valeria: agora, ou na virada do ciclo.
isDowngrade diz se o destino é mais barato que o plano atual.
Recusa pelos mesmos motivos da troca real
Destino incompatível, assinatura em estado que não permite, rebaixamento pedido como imediato: tudo responde409 aqui também, com o mesmo motivo — e sem consumir a troca.
A rota é leitura pura. Chamá-la duas vezes com o mesmo corpo devolve o mesmo resultado, e ela não aceita
Idempotency-Key.Authorizations
API Key da Credential (gerada no Backoffice)
Path Parameters
ID da assinatura
Body
application/json
Plano de destino. Ausente ⇒ o destino é avulso e items é obrigatório.
Minimum string length:
1Itens do destino avulso (referência ou inline). Ignorado quando há planId.
- Option 1
- Option 2
Show child attributes
Show child attributes
Override de quantidade por componente do plano destino.
Show child attributes
Show child attributes
Quando a troca vale. Ausente ⇒ imediata para plano mais caro, na virada para mais barato. Pedir now num plano mais barato é recusado.
Available options:
now, period_end none troca agora sem cobrar a diferença (cortesia). Só vale com effectiveAt=now.
Available options:
create_prorations, none Response
Simulação da troca