---
title: Payment detail, events, and allocation
summary: Creates and moves an online payment through its state-machine lifecycle (pending, remitted, returned, cancelled), logs each transition as an immutable event, and bridges payments to session allocations.
status: draft
owner: SAIF Doc Gen
last_reviewed: 2026-07-30
library: SAIF.Business.Payment.EFT
reference_type: package
operation_id: OP-EFT-05
trigger: Caller creates a clsPaymentDetail via NewPaymentDetail, or invokes a state-transition method (PostPending, RefundVendorRemitted, ReturnVendorRemitted, CancelPayment, DeletePending, RemitPosted) on an existing payment
---

# Payment detail, events, and allocation

`clsPaymentDetail` implements `IOnlinePayment` and is the central record of a single electronic payment. Rather than a simple status column update, each state change is performed by a dedicated method (`PostPending`, `RefundVendorRemitted`, `ReturnVendorRemitted`, `CancelVendorPending`/`DeclineVendorPending`/`ReplaceVendorPending` via `UpdateVendorPending`, `DeletePending`, `RemitPosted`, `CancelPayment`) that updates the payment row and appends an immutable `clsPaymentEvent` audit record. Related bridge objects (`clsPaymentAllocation`, `clsSessionTransfer`, `clsPaymentAuthorizationSessionTransfer`) connect a payment to the accounting session(s) it was allocated from, and IAT/domestic payments trigger addenda creation (see [Payment addenda](payment-addenda.md)).

| Field | Value |
| --- | --- |
| **Operation ID** | OP-EFT-05 |
| **Trigger** | Caller calls `NewPaymentDetail`, or invokes a lifecycle method on a retrieved `clsPaymentDetail` |
| **Inputs** | Bank account, payee, amount, ACH standard entry class code, IAT indicator, warehoused/non-warehoused indicator, allocation/session-transfer references |
| **Outputs** | Persisted/updated `PAYMENT_DETAIL` row; one or more `PAYMENT_EVENT` audit rows per transition; `PAYMENT_ALLOCATION`/`SESSION_TRANSFER`/`PAYMENT_AUTH_SESSION_TRANSFER` rows; triggered addenda creation for IAT/standard payments |
| **Primary components** | `clsPaymentDetail` (lifecycle methods, `CreateIATAddenda`, `CreateStandardAddenda`), `clsPaymentDetailCollection` (5 retrieval builders), `clsPaymentAllocation`/`Collection`, `clsSessionTransfer`, `clsPaymentAuthorizationSessionTransfer`, `clsPaymentEvent`/`Collection`/`Code` |
| **Entry point** | `Business\SAIF.Business.Payment.EFT\clsPaymentDetail.vb` |

## Steps

1. **Create a new payment.** `NewPaymentDetail` fetches the next sequence value ([SQL-006](../sql/SQL-006-saif-business-payment-eft.md), [DUAL](../database-tables.md#dual)) and initializes bank account, amount, and status fields.
2. **Validate the ACH standard entry class code.** `ValidAchStdEntryClassCode` requires a standard entry class code only when the linked bank account's banking network is ACH; non-ACH accounts (e.g., debit card) are exempt ([BR-EFT-10](../business-rules.md#br-eft-10-ach-standard-entry-class-code-is-required-only-for-ach-banking-network-accounts)).
3. **Insert.** `BeforeInsert`/`AfterInsert` executes `INSERT INTO PAYMENT_DETAIL (...)` ([SQL-018](../sql/SQL-018-saif-business-payment-eft.md), [PAYMENT_DETAIL](../database-tables.md#payment_detail)).
   - On failure (`RowsAffected <> 1`): adds an `InsertError` message ([BR-EFT-05](../business-rules.md#br-eft-05-insertupdate-operations-require-exactly-one-affected-row)).
4. **Retrieve via one of five retrieval builders**, dispatched by `Criteria.RetrieveType`: `BuildSqlNormal` (standard retrieval by `PaymentDetailSeqNum`), `BuildSqlSeqNum` (retrieval by an explicit sequence-number list), `BuildSqlPend` (payments still in a pending state, used by downstream batch processing), `BuildPreNoteSql` (prenote-only payments), and `BuildPostedSql` (already-posted/remitted payments). The SQL catalog documents these collectively under a single retrieval entry that joins `PAYMENT_AUTHORIZATION` and `AUTHORIZATION_TYPE_CODE` ([SQL-059](../sql/SQL-059-saif-business-payment-eft.md), [PAYMENT_DETAIL](../database-tables.md#payment_detail)); it does not enumerate each of the five builder variants as a separate SQL ID.*
5. **PostPending, RefundVendorRemitted, ReturnVendorRemitted, and UpdateVendorPending (Cancel/Decline/Replace).** Each of these state-transition methods updates the payment row with its resulting status ([SQL-085](../sql/SQL-085-saif-business-payment-eft.md), [PAYMENT_DETAIL](../database-tables.md#payment_detail)) and appends a matching `clsPaymentEvent` row of the corresponding event code ([SQL-019](../sql/SQL-019-saif-business-payment-eft.md), [PAYMENT_EVENT](../database-tables.md#payment_event)). `ReturnVendorRemitted` is distinct from the fuller [Payment return handling](payment-return-handling.md) flow, which additionally creates a `PAYMENT_RETURN` record for ACH-network returns.
6. **DeletePending.** Removes a payment still in pending status; performs a physical delete on `PAYMENT_DETAIL` ([SQL-096](../sql/SQL-096-saif-business-payment-eft.md)) and logs a `DeletePending` event ([SQL-019](../sql/SQL-019-saif-business-payment-eft.md), [PAYMENT_EVENT](../database-tables.md#payment_event)).
7. **RemitPosted.** Marks a posted payment as remitted (funds actually sent); updates the row ([SQL-085](../sql/SQL-085-saif-business-payment-eft.md)) and logs a `RemitPosted` event ([SQL-019](../sql/SQL-019-saif-business-payment-eft.md)).
8. **CancelPayment.** Branches on payment source:
   - If `IsUSBankPayment`, delegates the cancellation call to `USBankAPIs.CancelPayment`.
     > **Origin:** Delegated to `SAIF.Business.EPay.USBank` — see [Recurring ACH authorization lifecycle](../../saif-business-epay-usbank/operations/recurring-ach-authorization-lifecycle.md).
   - Otherwise, cancels the payment locally by updating the row ([SQL-085](../sql/SQL-085-saif-business-payment-eft.md)) and logging a `CancelPayment` event directly ([SQL-019](../sql/SQL-019-saif-business-payment-eft.md)).
9. **Trigger addenda creation.** After certain state transitions (typically at insert/post time), IAT payments call `CreateIATAddenda()`, which creates 7 addenda records (type codes 10–16, using country codes rather than country names per the 2014 NACHA IAT format change) ([SQL-005](../sql/SQL-005-saif-business-payment-eft.md), [SQL-015](../sql/SQL-015-saif-business-payment-eft.md), [PAYMENT_ADDENDA](../database-tables.md#payment_addenda)); non-IAT payments with addendum text call `CreateStandardAddenda()` to create a single addenda record. See [Payment addenda](payment-addenda.md) for the full addenda lifecycle.
10. **Allocate a payment against a session.** `clsPaymentAllocation` links a payment to a `clsSessionTransfer` via `SessionTransferSeqNum`; insert ([SQL-016](../sql/SQL-016-saif-business-payment-eft.md), [PAYMENT_ALLOCATION](../database-tables.md#payment_allocation)), collection retrieval ([SQL-057](../sql/SQL-057-saif-business-payment-eft.md)), update ([SQL-084](../sql/SQL-084-saif-business-payment-eft.md)), and delete ([SQL-095](../sql/SQL-095-saif-business-payment-eft.md)) follow the standard CRUD pattern. Aggregation properties on the collection compute both dynamically-calculated and GW/WCIS non-dynamic balance amounts across allocations.
11. **Session transfer bridge.** `clsSessionTransfer` bridges `clsPaymentDetail` and `clsPaymentAllocation`; insert ([SQL-021](../sql/SQL-021-saif-business-payment-eft.md), [SESSION_TRANSFER](../database-tables.md#session_transfer)), retrieve ([SQL-076](../sql/SQL-076-saif-business-payment-eft.md)), and update ([SQL-087](../sql/SQL-087-saif-business-payment-eft.md)) follow the standard pattern. `BeforeDelete` enforces that the transfer is still in `TransactionPending` state and cascades a `Save()` of its child `PaymentAllocations` collection before allowing the delete to proceed ([SQL-097](../sql/SQL-097-saif-business-payment-eft.md)).
12. **Auto-pay session scheduling.** `clsPaymentAuthorizationSessionTransfer` is an insert ([SQL-017](../sql/SQL-017-saif-business-payment-eft.md)) / retrieve ([SQL-058](../sql/SQL-058-saif-business-payment-eft.md)) -only junction table (no update/delete) keyed by `DueDate`, used to schedule which session an auto-pay authorization's next payment will be drawn from ([PAYMENT_AUTH_SESSION_TRANSFER](../database-tables.md#payment_auth_session_transfer)).
13. **Immutable event log.** `clsPaymentEvent` records are append-only: `DeleteSQL` is intentionally disabled and throws "This object never deletes events" if invoked ([PAYMENT_EVENT](../database-tables.md#payment_event)); a single event can be retrieved by sequence number, joined with `PAYMENT_EVENT_CODE` and `PAYMENT_RETURN_REASON_CODE` descriptions ([SQL-060](../sql/SQL-060-saif-business-payment-eft.md)), or all events for a payment detail can be retrieved as a collection ([SQL-063](../sql/SQL-063-saif-business-payment-eft.md)); `clsPaymentEventCode` is a static, read-only lookup of the valid event-code values (see [Code table lookups](code-table-lookups.md)).

## Process flow

```mermaid
flowchart TD
	Start([Caller creates a payment]) --> Insert[INSERT PAYMENT_DETAIL]
	Insert --> IAT{IAT payment?}
	IAT -->|Yes| IATAddenda[CreateIATAddenda: 7 records]
	IAT -->|No addendum text| StdAddenda[CreateStandardAddenda: 1 record]
	IAT -->|No| Pending[Payment pending]
	IATAddenda --> Pending
	StdAddenda --> Pending
	Pending --> Transition{State transition}
	Transition -->|PostPending| Post[Update row; log event]
	Transition -->|CancelPayment| USBank{IsUSBankPayment?}
	Transition -->|DeletePending| DelPend[Physical delete; log event]
	Transition -->|RemitPosted| Remit[Update row; log event]
	USBank -->|Yes| Delegate[Delegate to USBankAPIs.CancelPayment]
	USBank -->|No| LocalCancel[Update row locally; log event]
	Post --> Return{Return/refund event?}
	Return -->|ReturnVendorRemitted| RVR[Update row; log event]
	Return -->|RefundVendorRemitted| RfVR[Update row; log event]
	Post --> Allocate[Allocate to session via<br>clsPaymentAllocation/clsSessionTransfer]
	Allocate --> Done([Lifecycle event logged])
```

## Failure and edge-case behavior

- Every state-transition method logs an immutable `PAYMENT_EVENT` row; events can never be deleted once created — `clsPaymentEvent.DeleteSQL` always throws.
- `ValidAchStdEntryClassCode` only requires the entry class code for ACH-network bank accounts; debit-card and other non-ACH accounts are exempt, so a blank entry class code is valid in those cases ([BR-EFT-10](../business-rules.md#br-eft-10-ach-standard-entry-class-code-is-required-only-for-ach-banking-network-accounts)).
- `clsSessionTransfer.BeforeDelete` refuses to delete unless the transfer is still `TransactionPending`, and always attempts to save its child `PaymentAllocations` first — a failure in the child save can block the parent delete.
- `CancelPayment` behavior differs materially by payment source: US Bank payments are cancelled through an external API call (which may fail independently of local database state), while non-US-Bank payments are cancelled purely locally.
- `clsPaymentAuthorizationSessionTransfer` supports insert and retrieve only — there is no update or delete path, so a scheduling mistake must be corrected by inserting a new row rather than modifying the existing one.*
- The five `clsPaymentDetailCollection` retrieval builders are selected by an enum and are not interchangeable; calling the wrong `RetrieveType` for a given scenario (e.g., using `BuildSqlPend` to find posted payments) will return an empty or incorrect result set rather than raising an error.*

## Related documentation
- [Operations and processes](../operations-processes.md)
- [Business rules](../business-rules.md)
- [SQL catalog](../sql/index.md)
- [Database tables](../database-tables.md)

> **Note:** Statements marked with `*` were inferred from code structure, naming conventions, or contextual evidence where direct confirmation was not available in source artifacts.

## Sources
- `Business\SAIF.Business.Payment.EFT\clsPaymentDetail.vb`
- `Business\SAIF.Business.Payment.EFT\clsPaymentDetailCollection.vb`
- `Business\SAIF.Business.Payment.EFT\clsPaymentAllocation.vb`
- `Business\SAIF.Business.Payment.EFT\clsPaymentAllocationCollection.vb`
- `Business\SAIF.Business.Payment.EFT\clsSessionTransfer.vb`
- `Business\SAIF.Business.Payment.EFT\clsPaymentAuthorizationSessionTransfer.vb`
- `Business\SAIF.Business.Payment.EFT\clsPaymentEvent.vb`
- `Business\SAIF.Business.Payment.EFT\clsPaymentEventCollection.vb`
- `Business\SAIF.Business.Payment.EFT\clsPaymentEventCode.vb`
