Skip to main content

Handling lost or unknown payment results

For POS integrators: what to do when your POS doesn't get a payment result, how to check the outcome before taking a new payment, and how to undo a payment you can't register. Covers AppSwitch and the Cloud API.

Who this is for

This article is for POS integrators using the AppSwitch SDK or the Cloud API. It explains what to do during a sale when your POS does not know whether a payment went through. For getting all transaction data into your own system, see How to build your own transaction reporting using Softpay transaction data.

Why results get lost

When a POS does not receive a payment result, the payment has almost always reached a final state on Softpay's side within seconds. It is the result that did not make it back to your POS. Common causes:

  • Your app was closed, restarted or killed by the system during the payment.

  • The device lost network connection.

  • The Softpay app did not switch back to your app in time, and your app stopped waiting.

In all these cases, Softpay knows the outcome and you can look it up.

The golden rule: check before you charge again

Never start a new payment for the same order until you have checked the previous one. If the first payment was approved and you take a second one, the customer is charged twice. This is the most common cause of double charges we see.

When the outcome is unclear (a timeout, an app restart, an unexpected error after the card was read), your POS should:

  1. Look up the previous payment (see below).

  2. If it is completed, register the sale. Do not take a new payment.

  3. If it is declined, cancelled or failed, it is safe to start a new payment for the same order.

  4. If you cannot get an answer, do not guess. Let staff check the payment in the Softpay app before charging again or returning money.

Step 1: save the requestId before the payment starts

Every payment has a requestId that you set when you start it. Save it before the payment starts, ideally in your backend, not only on the device. If you only save it together with the result, you have nothing to look up when the result is lost.

Step 2: look up the outcome

AppSwitch

  • onSuccess and onFailure only tell you whether the request was delivered, not what happened to the payment.

  • Use onFinal as the definitive outcome.

  • If onFinal never arrives, call GetTransaction with the requestId. Do this after a timeout, after your app restarts, and after any unclear error.

  • If the lookup returns null, the payment was cancelled before a card was tapped. That is expected, not an error, and no money was taken.

Cloud API

  • Look up the transaction with GET /transactions/{requestId}.

  • COMPLETED, DECLINED, CANCELLED and FAILED are final states. A COMPLETED transaction only changes if it is cancelled afterwards, in which case it becomes CANCELLED.

  • If the state is PROCESSING, wait and look it up again. Processing for more than a few seconds is rare and is usually caused by a network outage. Softpay's backend then tries to reverse the transaction automatically.

  • The lookup shows the state at the time of the call. A transaction that is still processing can change right after you check it.

If a transaction never reaches a final state, your POS has to decide how to treat it. Treating it as paid carries a small risk of not getting paid. Treating it as cancelled carries a small risk that the customer was charged. Make this a deliberate choice, and let staff confirm in the Softpay app where possible.

Making result delivery more robust (AppSwitch)

  • Set switchBackTimeout. It controls how long the Softpay result screen is shown before switching back to your app automatically. Without it, the switch back depends on someone tapping OK. Choose a value that still gives the customer time to see the result.

  • Use the early result if you need it, but register the sale on onFinal. The early result arrives a few seconds before onFinal, but onFinal (or a lookup showing completed) is the outcome to register the sale on.

  • One payment at a time. The Softpay app processes one transaction at a time, with no queue. Make sure your POS cannot send a second payment request while one is still in progress, for example if staff go back and press "Pay" again.

Cloud API: when a previous transaction is still active

A transaction that has not finished blocks new ones on the same Softpay app. If the app never picks up a transaction, the Cloud API unblocks it automatically after 3 minutes. To unblock it sooner:

  1. Call GET /transactions/active (optionally with appId) to find the active transaction.

  2. Call PUT /transactions/{requestId}/abort with its requestId.

A transaction can only be aborted before a card is tapped, and even then not always, depending on how far the payment flow has got. If aborting is not possible, the call returns a failure. Aborted transactions show as FAILED. A completed transaction cannot be aborted. Use a cancellation instead (see below).

Undoing a payment you cannot register

Sometimes a lookup shows the payment was approved, but your POS cannot complete the sale. Undo the payment straight away, and do not leave it for later:

  • Cancellation (preferred): needs no card tap. It only works on the terminal's latest transaction and within 10 minutes of the payment. It is available through AppSwitch and the Cloud API, and through the Cloud API it works even if the device is offline. A cancelled payment is undone before it settles, so the customer does not see a charge and a refund on their statement. Depending on their bank, the pending amount may take a little while to disappear. Cancellation requires the cancellation privilege, which is normally enabled for payments started from a POS app.

  • Refund: after 10 minutes, or once another transaction has been made on that terminal, the money has to be returned with a refund. In Softpay, a refund normally requires the card to be tapped. Card-not-present refunds through the Cloud API are only supported by some processors and need details from the original payment that must be saved at the time of payment. See the developer documentation before relying on them.

Developer documentation on cancellations and refunds: Cloud API operations

Is a completed payment final?

Once Softpay reports a transaction as completed, it only changes on Softpay's side if you cancel it. In rare cases the acquirer can still reverse a payment afterwards, and cardholders can raise chargebacks through their bank. So "completed" is safe to register the sale on, but reconcile your books against the acquirer's settlement reports, which are the final financial record.

Safety nets

Testing before go-live

Test these scenarios in sandbox:

  • Kill your app while a payment is processing, then confirm it recovers the outcome using the requestId.

  • Make an approved payment where your POS does not receive the result, and confirm it finds the payment instead of taking a new one.

  • Cancel an approved payment within 10 minutes.

See also the Integration Self-Assessment, sections 3.3 and 4.3.

Did this answer your question?