iQpay Internal

Platform internals

Card events & transactions

Everything that happens to a card lands in cardevents. That table is where support questions get answered, so learn its shape well.

ℹ️
Portal naming.

The menu item is Activity. The page is titled Transactions for Bridge Admins and Redemptions for org users. All of it is this one table. The only real difference is the Excel export: the redemption export filters to Purchase, Return, and Void events.

How events arrive

InComm decides at the register, then tells us afterward. The pipeline:

  1. InComm's webhook posts the result to our ingest service. We store the raw body in incommtransactioningests and acknowledge immediately. Nothing is processed on the request.
  2. A background job picks up pending ingests in batches (100 at a time, 10 in parallel), with retries and a manual-review state for failures.
  3. The job writes the cardevents row (idempotent per event id), the per-item incommskuleveltransactions rows, and updates the card balance. For prefunded programs it also decrements programs.TotalCurrentBalance.

Consequence: portal activity always lags the register by a little, and a stuck job means missing events, not missing purchases.

ℹ️
For troubleshooting, cardevents is the table.

It carries everything that matters: the result, the retailer, the amounts, the purses, and the per-item detail. Start there and usually end there. You rarely need incommtransactioningests; it is only the raw webhook queue, useful when you suspect an event never got processed at all.

The 13 event types

TypeMeaning
CreationThe card row was created.
ActivationThe member activated the card.
IssuerActivationThe same event as Activation. The name is Fiserv's: Walmart's processor calls its activation transactionType "issuerActivation", and we keep the label.
FirstTransactionFirst spend on the card.
PurchaseAn approved purchase.
ReturnA refund at the register.
VoidA voided transaction. The portal can display a void-of-return as "Purchase", so check the raw event.
HardDeclineInComm refused the transaction. See the decline table below.
ExpirationThe card expired.
DeactivationThe card was deactivated.
CardUnload / BalanceAdjustment / ForcedAdjustmentBalance was removed or adjusted outside a purchase.

Inside SourceData

cardevents.SourceData is a JSON document stored as text in MySQL. Its useful branches:

PathWhat you find there
resultcode ("Approval" or a decline code), description, timestamp.
originationretailerId, retailerName, store, lane, local time. store is the retailer's own store number, not a location. lane is the register.
transactionAmounts: original, clamp, approved. Plus authorization id and type.
cardholderpan and ingenId.
marketBasket.purses[]One entry per purse touched. Carries the purse name, remaining balance, amount authorized, and the covered items[].
marketBasket.purses[].items[]Per covered item: code (a 14-digit InComm UPC), quantity, redemption amount, price adjustment.
⚠️
Unqualified items are never echoed.

The items[] array lists only items the purse covered. On a decline the array is empty, and that emptiness proves nothing about what the member scanned. Use receipt arithmetic to reason about the rest of the basket.

Per-SKU rows are also denormalized into incommskuleveltransactions (one row per item, joined by CardEventId). Walmart redemptions arrive by settlement file into walmartfileimporttransactions instead. See The Walmart triangle.

Hard declines

The decline reason sits in SourceData.result.description (the code field just says HardDecline). Newer events carry plain-English labels; older ones carry camelCase IngenDecline* names for the same reasons. The full set seen in production, measured 2026-08-03 across 10,673 hard declines:

Reason (as shown)Older labelMeaning
UNMET ITEM QUALIFICATIONIngenDeclineUnmetQualificationNo eligible items in the basket. Stores report it as "APL - No Eligible Items".
INSUFFICIENT BALANCEAccountErrorInsufficientFundsThe purse cannot cover the eligible amount.
CLIP DELETEDIngenDeclineClipDeletedThe clip was removed from the customer. See the clip-deleted orphan trap in The clip lifecycle.
Card/PIN/ESN is InvalidThe card details were entered wrong at the register. Usually cashier handling.
CUSTOMER STATUS INACTIVEThe card is not active: never activated, deactivated, or expired.
RETAILER NOT INCLUDEDThe store's banner is not on the offer's banner list.
CUSTOMER LIMIT EXCEEDEDAn offer limit (clip, purchase, customer) was hit.
Transaction not supported on Partner / external system errorAn InComm-side or retailer-side processing failure, nothing to fix on ours.
⚠️
UNMET ITEM QUALIFICATION is not always real.

Sometimes this decline fires even though the basket did contain eligible items. That is an InComm-side problem we are working with them to resolve. Until it is resolved, treat this code as a starting point, not proof that the items were ineligible; this page will be updated when the fix lands.

⚠️
Old events swap two fields.

In records from before July 2025, the decline code and description fields are swapped. Query both fields when you search history.

Retailer and store identification

  • Retailer name: incommskuleveltransactions.RetailerName, or SourceData.origination.retailerName, or a readable SourceLocationCode like retailerId-retailerName.
  • Retailer id: RetailerId / SourceData.origination.retailerId.
  • Store: only the retailer's internal store number. A numeric code alone cannot give you banner, city, or state without an external mapping.
iQpay internal. Do not share outside the company.