Garu

2026-09-11

v0.23.0 — Ofertas: vários preços e vários links para o mesmo produto

produtoscheckoutapiafiliados

O que mudou

Um produto, vários preços

Na aba do produto, a seção Preço deixou de ser uma linha só. Agora ela lista o preço padrão e todas as ofertas que você criar:

NomePreçoLink
PadrãoR$ 497,00/pay/seu-produto
Black FridayR$ 97,00/pay/seu-produto?offer=black-friday
Lista VIPR$ 197,00/pay/seu-produto?offer=lista-vip

Uma oferta muda apenas o preço. Formas de pagamento, parcelamento, carnê, nome, descrição e imagem continuam vindo do produto — você configura uma vez e todas as ofertas seguem.

Nada muda no que já está no ar

O link que você já divulgou continua funcionando exatamente como antes, cobrando o preço padrão do produto. Ofertas só entram em jogo quando o link carrega ?offer=.

Ligar e desligar quando quiser

Toda oferta pode ser desativada e reativada. Acabou a Black Friday? Desative — quem abrir o link vê o preço normal do produto com um aviso de que aquela oferta encerrou, e ainda consegue comprar. Você não perde a venda de quem chegou atrasado, e ninguém é surpreendido por um preço diferente do prometido sem explicação.

Afiliados acompanham a oferta

A comissão é calculada sobre o que foi efetivamente cobrado. Numa oferta de R$97 com 10% de comissão, o afiliado ganha R$9,70 — não os R$49,70 do preço cheio. E o link de afiliado aceita oferta: /pay/link-do-afiliado?offer=black-friday credita o afiliado e aplica a oferta.

Dá para saber qual oferta vendeu

Cada venda guarda a oferta que a originou. Comparar "a Black Friday vendeu 40 e o preço padrão vendeu 6" passa a ser uma pergunta que os dados respondem.

Um aviso sobre o identificador do link

O identificador que você escolhe (?offer=black-friday) é público e fácil de adivinhar. Quem tem o link do produto pode tentar ?offer=promo, ?offer=desconto e encontrar um preço que você queria mandar só para uma lista.

Por isso ele é opcional. Para um preço que não deve circular, deixe o campo em branco: a oferta ganha um identificador próprio, longo e aleatório, que ninguém acerta por tentativa. Desativar a oferta também encerra o acesso na hora.

Duas travas de preço

Se o preço mudar enquanto o comprador preenche o formulário — você editou a oferta, ou o preço do produto — a cobrança é recusada e a página pede para atualizar. Ninguém paga um valor que não viu.

E se o produto tem co-produtores com valor fixo, uma oferta não pode custar menos do que esses valores exigem. Um co-produtor de R$150 recebe R$150 independentemente do preço da venda: numa oferta de R$97 ele levaria mais do que a venda inteira e a sua conta ficaria negativa. O sistema recusa a oferta e diz qual é o preço mínimo.

Para quem integra

Novos endpoints, autenticados por API key:

POST   /api/v1/products/{productUuid}/offers
GET    /api/v1/products/{productUuid}/offers?active=true|false|all&page=1&limit=20
GET    /api/v1/offers/{offerId}
PATCH  /api/v1/offers/{offerId}
DELETE /api/v1/offers/{offerId}

Criando uma oferta:

curl -X POST https://garu.com.br/api/v1/products/$PRODUCT_UUID/offers \
  -H "Authorization: Bearer $GARU_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Black Friday", "value": 97.00, "slug": "black-friday" }'

value é em reais, igual ao preço do produto. O preço pode ser maior que o padrão — uma oferta serve tanto para desconto quanto para um link premium.

DELETE só funciona enquanto a oferta nunca vendeu. Depois da primeira venda ele responde 409: desative a oferta em vez de excluí-la, para que as vendas passadas não percam a atribuição.

Produtos de assinatura continuam usando ?priceId= e recusam ofertas com 400 — são dois seletores de preço, e misturar os dois num link só deixaria ambíguo qual vale.

POST /api/v1/charges também aceita offer, então dá para cobrar uma oferta direto pela API, sem precisar mandar o comprador pelo link hospedado:

{ "productId": "...", "offer": "black-friday", "paymentMethod": "pix", "customer": {} }

Quem usa os SDKs: @garuhq/node 5.2.0 traz garu.offers.* e charges.create({ offer }); o @garuhq/cli 0.14.0 traz garu offers; e o @garuhq/mcp 0.24.0 expõe as cinco ferramentas para agentes.

No checkout, POST /api/transactions aceita o campo opcional offer, com o identificador ou o id da oferta. Quem resolve o preço é sempre o servidor: o valor cobrado nunca vem do navegador.

Se a oferta tiver sido desativada entre a abertura da página e o pagamento, a cobrança responde 409 e não cria nada. Abrir o link com uma oferta encerrada mostra o preço padrão — nesse momento ninguém se comprometeu com nada. Mas depois que o comprador viu R$97 e apertou pagar, cobrar o preço cheio seria cobrar mais do que ele aceitou. Então a cobrança é recusada e a página pede para atualizar.