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

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.

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
        }
    ]
}

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.

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.

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.

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.

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

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