Saltar para o conteúdo
Última atualização

Recompensas e Resgates

As recompensas permitem-lhe transformar parte de um pagamento em créditos que o cliente pode gastar num pagamento posterior. Os créditos ficam numa conta de recompensas associada ao seu marketplace e seguem o cliente que os ganhou apenas esse cliente os pode resgatar, e os créditos expiram após um período definido (12 meses por defeito).

O fluxo tem dois lados. Gera recompensas anexando um bloco reward a um ou mais splits dentro de uma captura. Resgata-as mais tarde enviando um bloco reward_redemption na captura seguinte. Entre essas duas operações, pode consultar o que cada cliente tem disponível através dos endpoints de Customer. E se um pagamento for reembolsado, as suas recompensas são revertidas a secção Reverter um pagamento que usou recompensas, mais abaixo, explica isto em detalhe.

Como funcionam as recompensas

A conta de recompensas é por marketplace. É provisionada na configuração inicial não existe API para a criar e é referenciada pelo seu UUID em cada geração e resgate de recompensa.

Cada entrada numa conta de recompensas está associada a um cliente. Quando um pagamento inclui um split com um bloco reward, o cliente identificado no topo do pedido é creditado pelo valor em reward.value. Esse crédito passa a fazer parte do saldo disponível do cliente até ser resgatado ou expirar.

O resgate funciona no sentido inverso: numa captura posterior para o mesmo cliente, envia um bloco reward_redemption e o valor correspondente é debitado do saldo e aplicado ao pagamento.

Métodos de pagamento suportados

As recompensas dependem de um pagamento que confirma imediatamente, pelo que ambos os lados do fluxo só estão disponíveis nestes métodos:

Método de PagamentoCódigoRecompensas
Cartão de Crédito/DébitoCC✅
MB WAYMBW✅
Apple PayAP✅
Google PayGP✅
Samsung PaySW✅
Pagamentos PresenciaisIPP✅
MultibancoMB❌
IBAN VirtualVI❌
Débito DiretoDD❌

Gerar recompensas numa captura

Anexe um bloco reward a qualquer split dentro do array capture.splits[]. O cliente identificado no topo do pedido é o que recebe o crédito.

curl -X POST 'https://api.test.easypay.pt/2.0/single' \
  -H 'AccountId: SEU_ACCOUNT_ID' \
  -H 'ApiKey: SUA_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "type": "sale",
    "method": "cc",
    "value": 50.00,
    "currency": "EUR",
    "customer": {
      "id": "649e88cf-0b78-4c36-8f99-33f5ebb812a1",
      "name": "John Doe",
      "email": "john.doe@example.com"
    },
    "capture": {
      "descriptive": "Order #1029",
      "splits": [
        {
          "split_descriptive": "Marketplace seller",
          "account": {
            "id": "7e697e0c-c2bf-422a-9535-ab0b750bb832"
          },
          "value": 50.00,
          "reward": {
            "account": {
              "id": "458b2fc4-3092-4de3-abd4-fe1600c09420"
            },
            "value": 2.50,
            "expiration_date": "2027-04-02"
          }
        }
      ]
    }
  }'

O que cada campo de reward faz:

  • reward.account.id: a conta de recompensas do marketplace que detém o crédito. Use o mesmo UUID em todas as recompensas que pretende agregar no mesmo saldo.
  • reward.value: quanto desse split se transforma em crédito. Não altera o que o cliente paga no momento; é um valor que o marketplace contribui a partir do valor do split.
  • reward.expiration_date: opcional. Por defeito são 12 meses a partir da data da transação, e não pode ser definida para além do máximo de um ano. Depois desta data o crédito já não pode ser resgatado.

Ler o saldo de um cliente

Dois endpoints expõem o que um cliente tem.

GET /customer/{id} devolve o cliente juntamente com um array reward_balances com uma entrada por cada conta de recompensas em que tem créditos. Este é o saldo disponível autoritativo reflete resgates, reembolsos e reversões. Use-o quando quiser um total rápido antes de apresentar uma opção "Usar as suas recompensas" no checkout.

{
    "id": "649e88cf-0b78-4c36-8f99-33f5ebb812a1",
    "name": "John Doe",
    "email": "john.doe@example.com",
    "reward_balances": [
        {
            "account_id": "458b2fc4-3092-4de3-abd4-fe1600c09420",
            "available_balance": 12.5
        }
    ]
}

O available_balance pode ser negativo. Isto acontece quando é retirado cashback que o cliente já gastou, por exemplo porque o pagamento que o gerou foi reembolsado (ver Quando o cashback gasto é retirado, mais abaixo). Um saldo negativo é o valor de cashback que o cliente ainda deve, e significa que nenhum dos seus créditos tem valor por gastar.

GET /customer/{id}/rewards lista todos os movimentos individuais no histórico, do mais recente para o mais antigo. Suporta filtragem por conta, tipo, expiração e data de criação. Cada movimento tem um type:

  • REWARD: foi ganho um crédito.
  • REDEMPTION: um crédito foi gasto num pagamento.
  • REWARD_EXPIRATION: um crédito expirou.
  • REWARD_REVERSAL: um crédito ganho foi recuperado (o pagamento que o gerou foi reembolsado).
  • REWARD_RESTORATION: um crédito gasto foi devolvido (um resgate foi revertido).
curl 'https://api.test.easypay.pt/2.0/customer/649e88cf-0b78-4c36-8f99-33f5ebb812a1/rewards?type[]=REWARD&account_id[]=458b2fc4-3092-4de3-abd4-fe1600c09420' \
  -H 'AccountId: SEU_ACCOUNT_ID' \
  -H 'ApiKey: SUA_API_KEY'
{
    "metadata": { "next_cursor": null, "count": 2 },
    "data": [
        {
            "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
            "account_id": "458b2fc4-3092-4de3-abd4-fe1600c09420",
            "type": "REWARD",
            "amount": 10.0,
            "expiration_date": "2027-01-15",
            "capture": { "id": "c6056234-a3f9-42de-b944-3ed793fcb6bb" },
            "created_at": "2026-01-15T10:30:00Z"
        },
        {
            "id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
            "account_id": "458b2fc4-3092-4de3-abd4-fe1600c09420",
            "type": "REWARD",
            "amount": 2.5,
            "expiration_date": "2027-04-02",
            "capture": { "id": "b6f53027-0478-4728-9269-8bcc0f8088ea" },
            "created_at": "2026-04-02T09:12:00Z"
        }
    ]
}

Use o histórico quando precisar de um registo de auditoria por movimento por exemplo, quando um cliente pergunta de onde veio um determinado crédito, ou quando quer avisar sobre créditos prestes a expirar. Para o total disponível atual, prefira GET /customer/{id} é o valor real e atualizado. Os movimentos somam sempre esse saldo, incluindo quando é negativo.

Resgatar recompensas numa captura

Para gastar créditos, adicione um bloco reward_redemption à captura que está a pagar.

A localização do bloco depende do endpoint:

  • POST /single (pagamento num único passo) o bloco fica dentro do objeto capture: capture.reward_redemption.
  • POST /capture/{id} (captura de uma autorização anterior) o bloco fica na raiz do corpo da captura: reward_redemption.

O resgate é por conta de recompensas: um bloco reward_redemption aponta para um UUID de conta. O valor resgatado não pode exceder o saldo disponível nessa conta, nem o valor do próprio pagamento. Um resgate acima do saldo disponível é recusado com HTTP 412 e insufficient reward balance: available X.XX, requested Y.XX. Enquanto o saldo for zero ou negativo todos os resgates são recusados, por isso apresente a opção "Usar as suas recompensas" apenas quando available_balance for superior a zero.

Resgate parcial em POST /single aplicar apenas parte do saldo:

{
    "type": "sale",
    "method": "CC",
    "value": 50.0,
    "customer": {
        "id": "649e88cf-0b78-4c36-8f99-33f5ebb812a1"
    },
    "capture": {
        "reward_redemption": {
            "account": {
                "id": "458b2fc4-3092-4de3-abd4-fe1600c09420"
            },
            "value": 5.0
        }
    }
}

Resgate total em POST /single aplicar todo o valor do pagamento:

{
    "type": "sale",
    "method": "CC",
    "value": 50.0,
    "customer": {
        "id": "649e88cf-0b78-4c36-8f99-33f5ebb812a1"
    },
    "capture": {
        "reward_redemption": {
            "account": {
                "id": "458b2fc4-3092-4de3-abd4-fe1600c09420"
            },
            "value": 50.0
        }
    }
}

O value que envia é o bruto o valor antes das recompensas. O cliente é cobrado por value − reward_redemption.value através do método de pagamento escolhido (o valor de rails); a parte resgatada é coberta pelo saldo. O valor resgatado é debitado do saldo imediatamente e aparece como uma entrada REDEMPTION no histórico.

Quando o resgate iguala o valor total do pagamento (resgate total), o valor de rails é 0: nada é cobrado no gateway de pagamento e o pagamento é concluído na criação com payment_status = paid.

Ganhar e gastar no mesmo pedido

Um único pagamento pode fazer ambos. Em POST /single, o bloco reward_redemption fica dentro de capture e reduz o valor que o cliente paga; os blocos capture.splits[].reward creditam o cliente com base nos splits. Não interagem entre si pode resgatar €10 de um saldo anterior e ganhar €5 de novos créditos na mesma captura.

O exemplo Sale with Splits and Reward Redemption em POST /single mostra a forma combinada de ponta a ponta.

O que acontece quando os créditos expiram

Os créditos que um cliente nunca gasta não ficam indefinidamente. Um processo agendado encontra os créditos cuja data de expiração já passou e que ainda têm saldo positivo, marca-os como expirados (saldo disponível → 0) e paga o valor remanescente da conta de recompensas para a conta de payout do marketplace. A partir desse momento o cliente já não pode gastar o crédito, e o movimento aparece como REWARD_EXPIRATION no histórico do cliente.

Só expira a parte de um crédito que ainda não foi gasta. O valor gasto, ou usado para pagar cashback que o cliente devia, nunca expira nem é pago.

Quando o cashback gasto é retirado

Por vezes o cashback é retirado depois de o cliente já o ter gasto:

  • O pagamento que o gerou é reembolsado. O crédito ganho é recuperado (ver Reverter um pagamento que usou recompensas, mais abaixo).
  • O pagamento que o gerou sofre um chargeback. O crédito ganho é recuperado da mesma forma que num reembolso.
  • Um pagamento é confirmado tardiamente. Quando um pagamento que resgatou cashback falha ou é eliminado, o resgate volta ao saldo do cliente. Se o método de pagamento o confirmar mais tarde, o pagamento mantém o resgate completo, mesmo que entretanto o cliente tenha gasto esse cashback noutro pagamento.

Nos três casos, o valor é retirado primeiro do crédito a que pertence e depois dos outros créditos do cliente na mesma conta de recompensas, começando pelo que expira primeiro. Só o que os créditos não conseguem cobrir torna o saldo negativo. Cada crédito afetado aparece como um movimento próprio no histórico (REWARD_REVERSAL numa recuperação).

Exemplo. Um cliente ganha €4,00 na encomenda 1 (expira em junho) e €2,00 na encomenda 2 (expira em dezembro). Gasta €3,00 na encomenda 3, que usa primeiro o crédito de junho, ficando com €1,00 + €2,00 e um saldo de €3,00. A encomenda 1 é depois reembolsada na totalidade, pelo que os seus €4,00 têm de ser recuperados: €1,00 sai do crédito de junho, €2,00 do crédito de dezembro, e o €1,00 restante não pode ser coberto. O saldo passa a −€1,00 e ambos os créditos ficam gastos.

Regularizar um saldo negativo

A dívida mantém-se no saldo até voltar dinheiro para o cliente. O movimento seguinte que acrescenta dinheiro paga primeiro a dívida:

  • um novo crédito ganho num pagamento posterior (REWARD);
  • cashback restaurado por um reembolso ou chargeback de um pagamento que o resgatou (REWARD_RESTORATION);
  • um resgate devolvido porque o seu pagamento falhou ou foi eliminado. Não é escrito nenhum movimento novo: a entrada REDEMPTION simplesmente sai do histórico.

O movimento continua a mostrar o valor completo, e o saldo sobe por esse valor completo. No exemplo, se o cliente ganhar depois €3,00, o histórico mostra um REWARD de €3,00 e o saldo passa de −€1,00 para €2,00. Só €2,00 do novo crédito podem ser gastos, e só esses €2,00 podem expirar.

Reverter um pagamento que usou recompensas

Quando reembolsa uma captura que tinha recompensas, também é preciso revertê-las os créditos ganhos são recuperados, e qualquer cashback resgatado é devolvido ao cliente. Controla isto através de um bloco revert no reembolso.

Um reembolso com um bloco revert tem três partes:

  • value o bruto que está a reverter (rails + qualquer cashback resgatado). O valor de rails efetivamente devolvido e o cashback restaurado são derivados a partir deste e do campo de reversão abaixo.
  • revert.mode ou TOTAL (reverter a captura inteira) ou PARTIAL (reverter splits específicos).
  • revert.reward_redemption_reversal (opcional) quanto do cashback resgatado devolver ao saldo do cliente neste reembolso. O valor de rails devolvido ao pagador é value − reward_redemption_reversal.

Reversão total

Uma reversão TOTAL reverte todos os splits da captura. O value tem de ser igual ao bruto da captura. Se omitir reward_redemption_reversal, este assume por defeito o resgate remanescente (todo o cashback ainda não restaurado por um reembolso anterior), e o valor de rails devolvido é o restante.

{
    "value": 50.0,
    "revert": {
        "mode": "TOTAL"
    }
}

Reversão parcial

Uma reversão PARTIAL visa um ou mais splits por id. O value tem de ser igual à soma do bruto dos splits visados. O reward_redemption_reversal é um valor livre à sua escolha, retirado da pool de resgate da captura não está ligado à quota proporcional do split visado, pelo que uma única reversão parcial pode restaurar todo o resgate, se assim o pretender.

{
    "value": 12.0,
    "revert": {
        "mode": "PARTIAL",
        "reward_redemption_reversal": 5.0,
        "splits": [{ "id": "3f2a9c10-7b4e-4d21-9a55-0c1e2f3a4b5c" }]
    }
}

Aqui, €12 de bruto são revertidos no split visado, €5 de cashback são restaurados ao saldo do cliente, e 12 − 5 = €7 de valor de rails são devolvidos ao pagador.

Como funciona a pool de cashback

O reward_redemption_reversal restaura cashback a partir de uma pool partilhada o total resgatado na captura, menos o que reembolsos anteriores já restauraram. Ao longo de uma sequência de reembolsos parciais nunca pode restaurar mais do que o que foi originalmente resgatado. Quando restaura cashback, este volta ao saldo disponível do cliente imediatamente (visível em GET /customer/{id}) como um movimento REWARD_RESTORATION, e os créditos ganhos em qualquer split revertido são recuperados como REWARD_REVERSAL. Se o saldo do cliente for negativo, o cashback restaurado paga primeiro essa dívida.

A pool parte sempre do valor total resgatado na captura, mesmo quando o pagamento foi confirmado tardiamente depois de o seu cashback ter sido gasto noutro pagamento. Por isso, uma primeira reversão TOTAL desse pagamento pode restaurar todo o resgate.

Chargebacks em pagamentos com recompensas

Um chargeback reverte as recompensas da mesma forma que um reembolso, sem qualquer pedido da sua parte. Numa captura que gerou recompensas, os créditos ganhos são recuperados (REWARD_REVERSAL). Numa captura que resgatou cashback, o cashback resgatado é restaurado ao cliente (REWARD_RESTORATION). Um chargeback total restaura todo o resgate, e um chargeback parcial restaura a mesma proporção do resgate que a proporção do pagamento sujeita a chargeback. Um pagamento pago na totalidade com cashback não cobra nada no cartão, por isso não pode sofrer chargeback.

Verificação de saldo antes de uma reversão

Se a verificação de saldo do comerciante estiver ativa para o seu cliente (SPLIT_REFUND_BALANCE_CHECK_ENABLED), uma reversão só é aceite quando as contas a debitar têm saldo suficiente para a cobrir. Se não tiverem, a reversão é recusada (HTTP 412) com refund value can not exceed the balance of the debited accounts, try again later tente novamente quando o saldo estiver disponível.

Erros de validação

Dados de recompensa num método não suportado são recusados com HTTP 412. O campo é identificado tal como o endpoint o recebe:

  • POST /single: capture.reward_redemption: not supported for payment method MB
  • POST /capture/{id}: reward_redemption: not supported for payment method DD
  • POST /checkout: payment.capture.reward_redemption: not supported for payment method MB
  • Em qualquer dos três, quando a recompensa está num split: capture.splits[0].reward: not supported for payment method DD

Dados de recompensa num tipo não suportado são recusados da mesma forma:

  • POST /subscription: capture.reward_redemption: not supported for subscriptions
  • POST /checkout: payment.capture.reward_redemption: only supported when type is single

As reversões devolvem HTTP 412 com uma destas mensagens quando uma regra é violada:

  • revert.reward_redemption_reversal cannot be negative: a reversão é inferior a 0.
  • revert.reward_redemption_reversal: the capture has no reward redemption to revert: enviou uma reversão mas a captura nunca resgatou.
  • revert.reward_redemption_reversal cannot exceed the refund value: a reversão é superior ao value.
  • revert.reward_redemption_reversal exceeds the remaining redemption (X.XX): a reversão é superior ao cashback ainda disponível na pool.
  • revert.reward_redemption_reversal must equal the remaining redemption (X.XX) on a TOTAL revert: numa reversão TOTAL a reversão não é livre; tem de esgotar todo o resgate remanescente.
  • revert.splits: the value of the splits to revert X.XX is different than the requested refund value V: numa reversão PARTIAL, o value não corresponde ao bruto dos splits visados.
  • the rails refund (X.XX) exceeds the remaining refundable rails value (Y.XX): o valor de rails (value − reversal) excede o que ainda é reembolsável.
  • revert_mode TOTAL is not supported for partial refund: TOTAL enviado com um value inferior ao bruto da captura.
  • revert.mode PARTIAL requires at least one split id to be specified: PARTIAL enviado sem revert.splits.

Para consultar exatamente o que uma reversão restaurou, GET /refund/{id} expõe o objeto reward_redemption_reversal com value (restaurado neste reembolso) e value_remaining (cashback ainda restaurável na captura), juntamente com os objetos reward_redemption e reward (por split) da captura associada. GET /capture/{id} expõe igualmente o reward_redemption da captura e o reward de cada split.

Próximos Passos