Skip to main content

How to build your own transaction reporting using Softpay transaction data

How POS integrators and partners should access Softpay transaction data and build their own reporting — capturing final states during the payment flow, batch and Cloud API pulls, what to store, and the common pitfalls.

Audience: POS integrators, resellers and partners who need transaction reports for their own merchants — daily, monthly or on demand.

Short version: Softpay is not a reporting system. Softpay gives you the transaction data; the reporting belongs in your own system. The most reliable way to get it is to store every transaction in your own database at the moment it happens, and treat any API call as a backfill or repair mechanism — not as your primary source.

1. Your three options

Option

Best for

A. Capture in the POS during the payment flow

The recommended default. Full control, no polling, works for every merchant you serve.

B. Batch/API pull from your backend

Backfilling, nightly reconciliation, repairing gaps. Also the right option if your POS is thin and your backend owns the data.

C. Cloud API from a separate backend

When the system that needs the data is not the POS app at all (BI, ERP, accounting, a partner portal).

Most partners use A as the primary mechanism and B as a nightly safety net. Option C applies when the reporting lives entirely outside the POS.

2. Option A — capture the data during the payment flow (recommended)

If your POS is already integrated with the AppSwitch SDK and follows Softpay's recommended transaction handling, you already have every transaction and its final state. Reporting then costs you almost nothing extra: you are just persisting what already passes through your code.

The pattern:

  1. Generate and persist a RequestId before you start the payment. Store it in your backend, not only on the device. This is the key you will use for everything afterwards.

  2. Do not treat the request callback as the outcome. onSuccess / onFailure tell you the request was delivered — not what happened to the payment.

  3. Use onFinal as the definitive outcome. This is the moment a transaction reaches a final state.

  4. Write the transaction to your own reporting store at onFinal. Key it on RequestId so a repeated write is a no-op (idempotent).

  5. Use GetTransaction for recovery. If the app crashed, the process was killed, the network dropped, or onFinal never arrived, look the transaction up by RequestId and store the result. A lookup returning null for a transaction cancelled before the card tap is expected — not an error.

Do this and your reporting database is complete and correct by construction. Everything below is a safety net.

Rule of thumb: final state is the golden rule. The moment you know the final state, you may as well store it for reporting.

3. Option B — pull transactions in batches

For nightly reconciliation, gap-filling, or a POS whose backend owns the data, fetch transactions rather than capturing them.

  • AppSwitch batch fetch retrieves all transactions for a device in 24-hour batches. The batch number rolls over at 03:00 local time — this is not midnight, so align your report boundaries accordingly or you will chase phantom discrepancies.

  • Cloud API transactions endpoints let you filter by appId, batchNumber, and a time window (for example days=1 for the last 24 hours).

Run this as a scheduled job — e.g. every night, fetch yesterday's batch and reconcile it against what you already stored via Option A. Anything present in the batch but missing from your database is a gap worth investigating.

4. Option C — Cloud API from a separate backend

If the consuming system is not the POS app (BI warehouse, accounting, ERP, your own merchant portal), integrate the Cloud API server-to-server. Points that matter for reporting specifically:

  • OAuth client credentials are issued per integrator, and the same set is used across all your merchants. Handle token refresh automatically.

  • Your integrator account must be cloud-linked to every merchant whose data you want to read.

  • Never hard-code merchant references. Resolve them dynamically at runtime via the Cloud API /merchants endpoint or the Onboarding (MIA) API. Hard-coding works with one merchant and silently misattributes data as soon as a second one is onboarded — a mistake that is expensive to unwind after months of reports.

  • Sandbox and Production are separate environments with separate base URLs and separate credentials. Build and test in Sandbox first.

  • Webhooks can push transaction events to you in near real time instead of polling. Events may arrive out of order or more than once, so deduplicate on the transaction identifier and respond HTTP 200 promptly.

The Onboarding/MIA "Orders" API is for Softpay licences, not transactions. It will not give you transaction data — a common wrong turn.

5. What to store in your reporting database

Store the raw transaction data as you receive it, not just the fields your current report happens to need — requirements always grow. Typically available per transaction:

  • Your own RequestId, plus Softpay's transaction identifier

  • Timestamp (with timezone) and final state

  • Amount, currency, and where applicable tip and surcharge amounts

  • Card scheme and masked card number

  • Authorisation code, STAN, batch number

  • AID — needed if you must distinguish card products such as VPAY or Maestro from their parent scheme

  • Merchant, store, terminal and Softpay app ID (Descriptor.appId)

  • For failures: failure code, failure message and origin

Field availability varies between processors and acquirers. Handle missing/null fields gracefully rather than assuming a fixed schema, and test against every processor/acquirer combination in your deployment.

6. Five things that catch people out

  1. Acquirer reports are the financial source of truth. Your POS data proves a payment was attempted and what the terminal saw; the acquirer can still change an outcome afterwards (for example on fraud). Use your own data for operational reporting and reconciliation — settle the books against the acquirer.

  2. The Softpay app only keeps 10 days of history, tied to the current AppID. It is a lookup tool for staff, not a data source for reporting. If nobody stored the data, after 10 days it is gone from the app.

  3. The batch day starts at 03:00 local time, not midnight. Mixing batch boundaries with calendar days is the most common source of "missing" transactions.

  4. Refunds, cancellations and reversals are transactions too. Store them as their own records, linked to the original, or your totals will drift.

  5. Deduplicate on identity, not arrival. Retries, webhook redelivery and recovery lookups will all hand you the same transaction more than once.

7. Implementation checklist

  • RequestId generated and persisted in the backend before each payment

  • onFinal implemented and used as the definitive outcome

  • Transaction written to your reporting store at final state, idempotent on RequestId

  • GetTransaction recovery path implemented for crashes, kills and connectivity loss

  • Nightly batch or Cloud API sweep reconciling yesterday against your own records

  • Merchant references resolved dynamically — verified with at least two merchants

  • Report boundaries aligned to the 03:00 batch rollover, or deliberately not

  • Refunds and cancellations represented in the data model

  • Reporting output cross-checked against an acquirer report for one full period before go-live

8. References

Did this answer your question?