Tabby and Tamara refunds in Odoo: partial, invoice and POS
Refund a Tabby or Tamara payment from Odoo in full or in part: from the payment record, the invoice or the POS, and what happens when a POS refund syncs.
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 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 what stops one sale being refunded twice, and what to do when the gateway says no.
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.
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. A checkout that was authorised but never captured has nothing to refund; it is released instead.
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. 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 with the provider's message.
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.
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. 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:
- The module finds the negative Tabby payment lines on the refund order.
- It follows the refunded lines back to the original order and its captured session.
- 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.
- 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.
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 counter sale, use the POS refund flow above, where the lines set the amount.
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.
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, a manual button one derived from the session, and the web path the Odoo refund reference. A retry after a lost response reuses that key, so it reaches the provider as the same refund rather than a new one.
Locally, the session also keeps a list of the keys already applied. An offline POS that re-syncs the same refund order, a double click, or a second submission after a timeout all hit that list and are skipped. The same replay guard protects the capture side, as why a fake success URL cannot mark an Odoo order paid explains.
When the provider says no
A refund can be rejected. The common reasons are a payment that was never captured, a request larger than what remains, and credentials that do not match the environment the payment lives in. In the web path the refund transaction goes to error with the provider's message and the exchange is in the API log. In the POS path the message is stored on the session under Errors, the activity is scheduled, and the button retries with the same key.
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. 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.
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 counter sale, the cashier refunds the specific lines in the POS and the module sends that same amount to the provider on sync. The Refund on Tabby button on the invoice or the session refunds the whole remaining balance.
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?
No. Every refund carries an idempotency key set before the call goes out, and any retry reuses that same key. The session also keeps a local list of keys already applied, so a re-sync of the same refund order or a double click is skipped rather than counted again.