---
title: "Tabby and Tamara refunds in Odoo: partial, invoice and POS"
description: "Refund Tabby or Tamara from Odoo in full or in part, from the payment, invoice or POS, plus dashboard refunds Odoo never sees and what each error means."
lang: en
canonical: https://tabby.kerneltics.com/en/odoo-bnpl-refunds/
alternate_ar: https://tabby.kerneltics.com/odoo-bnpl-refunds/
published: 2026-09-17
updated: 2026-10-22
---

# Tabby and Tamara refunds in Odoo: partial, invoice and POS

> Refund Tabby or Tamara from Odoo in full or in part, from the payment, invoice or POS, plus dashboard refunds Odoo never sees and what each error means.

The first real test of a BNPL integration is not the first sale. It is the first return. A customer brings back one item out of three, someone has to give money back, and the money never came from the customer's card. It came from Tabby or Tamara. If the refund is recorded in Odoo but never reaches the provider, the customer keeps paying instalments for something they returned.

This article covers how refunds work in [Tabby & Tamara for Odoo](https://tabby.kerneltics.com/en/) from the three places a refund can start: the payment record for a web or portal sale, the Point of Sale, and the customer invoice. Then refunds made outside Odoo, what stops one sale being refunded twice, and what to do when a refund does not go through.

## What a BNPL refund actually is

With a card, a refund reverses a charge. With BNPL the provider paid you, or will pay you at settlement, and the customer owes the provider in instalments. A refund is you sending money back to the provider through its API; the provider then settles with the customer on its own terms. You never hand cash to a BNPL customer. How a refund then shows up in the provider's payout is covered in [the guide to reconciling BNPL payouts in Odoo](https://tabby.kerneltics.com/en/bnpl-accounting-odoo/).

Two consequences follow. A refund only exists once the provider has accepted it; a credit note in Odoo on its own changes nothing on the customer's plan. And only a captured payment can be refunded. The module captures a web payment as soon as Odoo learns the provider approved it, and a counter payment as soon as the register sees the approval, so in practice every paid sale is already captured and reversing one always means a refund.

That is also why cancelling is not refunding. Cancelling a paid sales order in Odoo, or reversing its invoice, changes Odoo's records and nothing at the provider. The customer keeps paying instalments until one of the paths below sends a refund and the provider confirms it.

## Path one: a web or portal sale, from the payment record

For a sale paid at website checkout, on a portal invoice or on a quotation, the module plugs into the standard Odoo refund flow rather than adding a parallel screen. Both providers declare partial refund support to the payment framework, so Odoo offers the full wizard.

Open the paid invoice and use the Payment Transaction smart button, or open the payment record linked to it. The payment record has a Refund button, visible to users with invoicing rights while any amount is still refundable. It opens a small wizard showing the payment amount, what has already been refunded, the maximum allowed, and an editable refund amount. Leave it at the maximum for a full refund, or type the value of the returned line for a partial one. The wizard is part of Odoo itself on versions 15 through 19.

When you confirm, Odoo creates a child refund transaction and the module calls the gateway: Tabby's refunds endpoint for a Tabby payment, Tamara's simplified refund endpoint for a Tamara one, each carrying your Odoo refund reference as the idempotency key. The refund travels in the currency of the original payment, so a dinar sale is refunded in dinars; [how that plays out in your books when the rate has moved](https://tabby.kerneltics.com/en/bnpl-currencies-gcc-odoo/) is covered in the guide to GCC currencies. Only an explicit confirmation from the provider marks the refund transaction as done. An error response, a network failure or an authentication failure marks it as error instead, with one of the messages explained further down.

A refund that ends in error does not use up the balance. When Odoo works out the maximum the wizard allows, it counts only refunds the provider confirmed, so once the cause is fixed the same Refund button offers the full amount again. Read the section on failed refunds before you press it a second time, though: one kind of failure needs a check first.

Once the refund transaction is done, Odoo's own post-processing creates the matching outbound payment in the provider's journal, so accounting sees money leaving the same account it arrived in. What the wizard does not do is create a credit note. If the sale itself is being reversed, issue the credit note as you normally would; the gateway refund and the accounting document are two separate acts, on purpose.

Every call in this path lands in the API log with credentials redacted, so a rejected refund can be read rather than guessed at. How to read that log is covered in [why Tabby disappears from Odoo checkout in SAR](https://tabby.kerneltics.com/en/odoo-payment-provider-sar/).

### On Odoo 14

Odoo 14 has no Refund button on payments. Odoo added refunds to its payment framework in version 15, and the module's 14.0 branch does not add its own for web sales. On 14, a web or portal sale is refunded with the provider directly and then recorded in Odoo by hand, as described under refunds made outside Odoo below. Counter sales on 14 are not affected: the POS, invoice and session paths that follow all exist there.

## Path two: a return at the counter

Counter sales are different because the payment was captured by the module's own POS flow, the QR flow described in [Tabby and Tamara in the Odoo Point of Sale](https://tabby.kerneltics.com/en/bnpl-odoo-pos/). The record that matters here is the Tabby payment session, under Point of Sale, Orders, Tabby Payments. Each captured counter payment has one.

### What the cashier does

Nothing unusual, which is the point. Open the orders list in the POS, find the original receipt, press Refund, pick the lines and quantities coming back, and choose the Tabby method on the payment screen. On a refund order no QR popup opens; the module adds a plain negative payment line and lets the cashier validate.

### What happens on sync

The work happens when the refund order reaches the server:

1. The module finds the negative Tabby payment lines on the refund order.
2. It follows the refunded lines back to the original order and its captured session.
3. It sends the provider a refund for the smaller of the refund amount and the balance still refundable on that session, with an idempotency key built from the refund order.
4. On success it adds the amount to the session's refunded total, flips the status to Refunded once the whole capture is covered, and posts a note in the session chatter naming the refund order.

Because the amount comes from the lines the cashier chose, a partial refund is simply a refund of fewer lines. Repeated refunds against one sale work too, up to the captured amount, since the session stays refundable until its balance is zero.

### If the call fails

The refund order still syncs. The module never blocks a POS sync on a gateway problem. Instead it stores the error on the session and schedules a To Do activity on that record for whoever synced the order, saying the automatic refund failed and asking them to retry from the button. That activity is the thing to watch in the first weeks after go-live.

One current limit worth knowing now: the automatic on-sync refund looks for Tabby payment methods. For a Tamara counter sale, open the session record and use the button described below, which sends the refund to Tamara's own endpoint. The refunds step of [connecting Tamara to Odoo, step by step](https://tabby.kerneltics.com/en/tamara-odoo-integration/) covers the Tamara side, including why that button carries Tabby's name.

### When the customer approves just as the cashier cancels

Pressing Cancel on the QR popup, or letting it time out, removes the line from the order and tells the server to drop the session. If the customer had not approved yet, that is the end of it. But the server captures a counter payment the moment its polling sees the approval, and a captured payment cannot be cancelled, only refunded. So if the approval lands in the seconds before the cashier gives up, the session stays Captured although the register no longer shows it, and a customer who then pays by card or cash has paid twice.

The check takes a minute at closing time. In Tabby Payments, open each session from the day that shows Captured and confirm it has a POS Order, and that the order really was paid with that method. A captured session with no matching payment line is refunded from its own Refund on Tabby button, by someone with the refund right described below.

## Path three: straight from the invoice, or from the session

For counter sales that were invoiced, you do not have to leave the invoice. The customer invoice carries a Tabby smart button that opens the linked payment session, and a Refund on Tabby button in its header. That button refunds the remaining captured amount of every session linked to that invoice, after a confirmation dialog. It is the fastest route when a customer calls after leaving the shop.

The same button lives on the session record itself, works for both providers, and appears only while the session is captured and something is still refundable. Both buttons refund the remaining balance rather than asking for an amount. For a partial refund on a Tabby counter sale, use the POS refund flow above, where the lines set the amount. A Tamara counter sale has no partial path from Odoo yet, so a partial return on one is arranged with Tamara directly.

## Refunds made outside Odoo

A refund issued anywhere other than Odoo, in the provider's own dashboard or through its support team, never reaches Odoo. The module's return and notification handling only re-checks payments that are still open. Once Odoo has a payment as done, a later notification from the provider changes nothing, and a counter session has no notification channel at all. So after a dashboard refund Odoo looks exactly as before: the payment still shows its full amount as refundable, the session's refunded amount is unchanged, and the clearing account still expects money that the next payout will not contain.

Two habits stop that from turning into a month-end puzzle:

- **Pick one place to refund, and make it Odoo.** Refunds that start in Odoo keep Odoo and the provider in step without anyone having to think about it. Write that down for whoever handles returns.
- **Never repeat a dashboard refund in Odoo.** Pressing Refund afterwards does not record the earlier refund. It sends another one, and whether the provider accepts it depends on the balance left on its side.

Some refunds have to happen outside Odoo: a web sale on Odoo 14, or a partial return on a Tamara counter sale. Record those in Odoo by hand, the way your accountant records any refund paid outside the system: a credit note if the sale is being reversed, and the money going out booked against the provider's clearing account rather than your bank, since the provider nets it off a payout. [The guide to reconciling BNPL payouts](https://tabby.kerneltics.com/en/bnpl-accounting-odoo/) explains that clearing account, and its month-end check is where a forgotten dashboard refund tends to surface.

## Who is allowed to press the button

Refunds move real money, so the manual buttons sit behind a dedicated access right, Tabby Refund, in the Tabby BNPL section of a user's Access Rights. Nobody has it after install; grant it to the specific people allowed to issue refunds. The check is enforced inside the method, not just hidden in the view, so it cannot be bypassed over RPC.

That right covers the Refund on Tabby buttons, which are the counter-sale paths. The web and portal refund in path one is Odoo's own button and follows Odoo's invoicing rights instead, so to narrow who can refund an online sale, narrow who holds invoicing rights.

The automatic refund that follows a cashier's POS refund does not need this right. It runs server side during the sync: a cashier is already allowed to refund a sale, and the module's job is to keep the provider in step.

## Why a double click cannot refund twice

Every refund carries an idempotency key that exists in Odoo before the HTTP call goes out, not after. A POS refund uses a key derived from the refund order and a manual button one derived from the session, so a retry of either after a lost response reuses the key and reaches the provider as the same refund rather than a new one. The web path keys each call on the reference of its refund transaction, and the wizard creates a new transaction every time it is confirmed. What keeps a web refund from going out twice is that Odoo never offers more than the confirmed balance, plus the one check described in the next section.

Locally, the session also keeps a list of the keys already applied. An offline POS that re-syncs the same refund order, or a second click after the first refund went through, hits that list and is skipped. The same replay guard protects the capture side, as [why a fake success URL cannot mark an Odoo order paid](https://tabby.kerneltics.com/en/odoo-bnpl-security/) explains.

## When a refund does not go through

The usual causes are a request larger than what remains at the provider, keys that do not match the environment the payment was made in, and a network that drops the call halfway. Odoo reports them with a handful of messages, and each one points somewhere different.

- **"The refund request was rejected by the provider."** Set on a web refund transaction. Despite the wording, it covers three cases: the provider refused without a readable reason, the keys failed authentication, or the call never got an answer. The API log tells them apart: `network-error` in the HTTP Status column means no answer came back, and 401 means the keys.
- **"The provider did not confirm the refund:"** followed by text. The provider answered and declined, and the text after the colon is its own reason.
- **"Tabby refund failed:"** or **"Tamara refund failed:"** followed by the reason. This comes from the invoice or session button, and the same reason stays on the session under Errors. When an automatic POS refund fails, the scheduled activity quotes it too.

The case to slow down for is a call that got no answer, because the provider may have processed it and only the reply was lost. On the session and invoice buttons a retry is safe, since it reuses the key. A web retry is not: it is a new refund transaction with a new reference, so the provider sees a second refund. Before retrying a web refund whose log row reads `network-error`, look the order up in the provider's dashboard and see whether the first attempt landed. [The guide to install errors and the API log](https://tabby.kerneltics.com/en/odoo-bnpl-install-errors/) reads that log row by row.

Sometimes there is nothing in Odoo to refund at all. If the customer approved but the capture failed, a web transaction ends in error with "Tabby authorised the payment but the capture failed" or "Tamara could not capture this payment", and a counter session shows Capture Failed. Odoo never took that money, so it has no refund to send. Check the order's state with the provider before you tell the customer anything.

What the module cannot tell you is how the provider treats its own fee on a refunded sale, or how quickly the customer's instalments are adjusted. Both follow your agreement with Tabby or Tamara. This is an independent integration by Kerneltics with no affiliation with or endorsement by either company, so read those terms in your merchant contract.

## Test it before the first real return

Add a refund to your go-live checklist, next to the first test sale in [the step by step guide to adding Tabby to Odoo](https://tabby.kerneltics.com/en/add-tabby-to-odoo/). In test mode, take one small order through each path you actually use: a web sale refunded from the wizard, a counter sale refunded through the POS, and an invoiced counter sale refunded from the invoice. Then open the session and the API log and read what was sent and what came back. While you are at it, agree with whoever handles returns that refunds start in Odoo and nowhere else.

A refund flow you have watched once is one you can trust when the customer is standing at the counter.

## Frequently asked

### Can I refund part of a Tabby or Tamara payment from Odoo?

Yes. For a web or portal sale, the Refund wizard on the payment record has an amount field where you enter the value of the returned line. For a Tabby counter sale, the cashier refunds the specific lines in the POS and the module sends that same amount to Tabby on sync. The Refund on Tabby button on the invoice or the session refunds the whole remaining balance, and for now it is the only Odoo route for a Tamara counter sale.

### If I issue a credit note in Odoo, is the customer refunded at Tabby automatically?

No. A credit note is an accounting document and does not talk to the provider. The actual refund happens when you use the Refund button on the payment, when a cashier refunds through the POS, or when you press Refund on Tabby from the invoice, and it only counts as done once the provider confirms it explicitly.

### What happens when a cashier refunds a Tabby sale in the POS?

They do a normal POS refund and pick the Tabby method; no QR popup opens. When the refund order syncs to the server, the module finds the original payment session and sends Tabby a refund for the same amount, recording the result on the session. If the call fails, the order still syncs and an activity is scheduled to retry from the button. For a Tamara counter sale, the refund is issued from the session's button.

### Who can issue a refund from the back office?

The Refund on Tabby buttons on the invoice and on the session are restricted to the Tabby Refund access right under the Tabby BNPL section of a user's Access Rights, and nobody has it by default. The automatic refund that follows a cashier's POS refund does not need the right, because it runs server side.

### Can the same refund be sent twice if I double click or the POS re-syncs?

Not from the POS or the Refund on Tabby buttons. Those refunds carry an idempotency key set before the call goes out, any retry reuses it, and the session keeps a local list of keys already applied, so a re-sync of the same refund order or a double click is skipped. The web wizard is the exception: each confirmation creates a new refund transaction with its own reference, so if a web refund failed with no answer from the provider, check the provider's dashboard before trying again.

### I refunded a customer in the Tabby or Tamara dashboard. Will Odoo update itself?

No. Odoo does not pick up refunds made outside it, so the payment still looks refundable and the clearing account expects money the payout will not bring. Record the refund by hand, with a credit note if the sale is reversed and the outgoing money booked against the provider's clearing account, and do not press Refund in Odoo for the same return, because that sends a second refund.
