# Orders and refund requests
The [orders_get](../tools/orders_get.md) and [refunds_get](../tools/refunds_get.md) operations compose one selected channel's list or detail.
## Choose the operation and target
Both operations require OWNER or DELEGATEE with MANAGE_ORDER. MANAGE_MONETIZATION, MANAGE_SALES_OVERVIEW and member permissions do not replace it. In unified hosting, first discover an approved channel target, describe the selected operation, then use execute with matching arguments.channelCode. Account targets cannot access either reader.
| Goal | Selection | Result |
| --- | --- | --- |
| Orders list | orders_get, omitted view or kind list | ORDERS: one page, applied view, pagination and nextPage |
| Order detail | orders_get, kind detail with observed oid | ORDER_DETAIL: record, selectedRecordId, buyerProfile and invoiceSummary |
| Refund requests | refunds_get, omitted view or kind list | REFUNDS: one page; optional ISSUED / REJECTED / REFUNDED status |
| Refund detail | refunds_get, kind detail with observed rid | REFUND_DETAIL: refund plus nested order, profile and invoice summary |
| Refund consumption | refunds_get, kind history with observed rid and type video or download | REFUND_HISTORY, scope guidance without a history read, or unconfirmed SERIES guidance with all history rows withheld |
| Monthly CSV | Either tool, kind download | COMMERCE_DOWNLOAD_CONSOLE_REQUIRED: human guidance, no PAY records or export |
List page is 1–1,000,000 and limit is 15, 30, 50, 100 or 300, defaulting to 1/300. Omit orders isCP for all source categories; true/false passes the source Channel Point filter. Both lists accept an order-number search or bounded member search. Orders use productModel=ALL for lists and SubscriptionProduct for detail, matching the source wrapper; do not label every list row as a membership. Filters/order-number matching remain source semantics. Refund status and a resolved buyer are additionally checked against returned rows.
## Member selection and enrichment
Member search requests the first 500 profiles, with type ALL. Exact channel profile distinctId matching the search text can resolve directly. Otherwise only a single match on a complete result set resolves automatically. Multiple, truncated or zero matches produce action_required / COMMERCE_BUYER_SELECTION_REQUIRED before any PAY request. Candidates contain id, profileDistinctId and nickname, alongside complete/total. Refine text for results outside the bounded page.
Repeat the same search with an observed profileDistinctId to select a candidate. This is the channel profile's distinct identifier, not its numeric id or global user's distinct ID. The bridge resolves the internal global ID and rechecks the selected search result before returning a list. Changed or newly ambiguous selection discards the financial observation. The selectedBuyer result contains profileId, profileDistinctId and nickname, without exposing the internal global buyer ID.
Each list queries profiles once for the distinct observed buyers, unless the page is empty, and adds buyerProfile to every record. The single-profile DTO has no independent global-buyer binding; that association relies on the selected channel-scoped endpoint, not a separate database identity proof. Detail separately reads the buyer profile and invoice summary. An observed enrichment has {status:"observed",value:...}; explicit null/missing mapping or HTTP 404 is not-found; HTTP 5xx or UPSTREAM_UNREACHABLE is unavailable. Caught transport errors include errorCode. Other errors, invalid projected data and checked mismatches are not converted to optional absence.
## Composition and request boundaries
Each financial observation obtains a fresh ISSUER authorization through POST /profitable/authorize with the selected channelId, then uses its ephemeral bearer for one fixed PAY GET. The private [operator configuration](payment-gateway.md#operator-configuration) and 30-second / 2 MiB PAY limits apply. Neither token nor key is accepted as tool input, and issuer credentials are not persisted.
| Observation | Source path and selectors |
| --- | --- |
| Orders list | PAY /api/v1/orders with page, limit, productModel=ALL; optional oid, resolved distinctId and isCP |
| Order detail | PAY /api/v1/orders/{oid} with productModel=SubscriptionProduct |
| Refund list | PAY /api/v1/refund-requests with page, limit; optional oid, resolved recipientId and status |
| Refund detail | PAY /api/v1/refund-requests/{rid} |
| Member search | Core GET /seller/api/v1/profiles, type=ALL, search, page=1, limit=500 |
| List profiles | Core POST /seller/api/v2/channel-management/global-profiles/find-channel-profiles with userDistinctIds; this is a read |
| Detail profile | Core GET /seller/api/v2/channel-management/global-profiles/{buyer}/find-channel-profile |
| Invoice summary | Core GET /seller/api/v2/channel-management/profitable/system-invoices/{siid}/summary |
A list or detail reads PAY twice around enrichment, compares normalized projected records and pagination, and checks the final channel/operator/credential context. Member selection is also resolved again for a selected member list. A changed second observation suppresses partial data. This is sequential comparison, not an atomic ledger snapshot. Enrichment itself is not reread. Unknown fields are stripped before comparison, so changes only to omitted fields are not detected.
A page must match requested page/limit and expected length from totalEntries. Record keys and numeric row IDs must be unique. nextPage is the following page when totalEntries indicates more rows, otherwise null. No automatic pagination occurs; the schema's maximum page still applies to subsequent input. Orders validate buyer/invoice references, refunds validate nested order and recipient identity, and detail must return the selected oid or rid.
## Returned fields and exact amounts
| Projection | Required fields and optional observations |
| --- | --- |
| Order | id, oid, userName, status, systemInvoice, pgcode, transactedAt and createdAt. Optional siid, delegationType, isSubscription, updatedAt and prices listed below. |
| Invoice in order | siid, name, price, recurring and namespace.productModel. Optional note, status, issuedAt and buyerCountryCode, which may be null. Recipient account identity is removed from output. |
| Order prices | Optional price, revenuePrice, originalPrice, promotionalDiscountPrice, delegatedPrice, taxFree, tax and balance. Absence is preserved. |
| Refund | id, rid, oid, status, nested order, requestedAmount, productName, transactedAt, createdAt and updatedAt. Optional delegationType, withPg, isPgApplied, reason and requesterComments. |
| Refund prices | Optional refundedAmount, requestedDelegationAmount, refundedDelegationAmount, pgRefundRequestedAmount, pgRefundedAmount and pgBalanceAmount. |
| Profile | id, distinctId and nickname; a channel profile, not an account credential. |
| Detail invoice summary | siid, isGuestCheckout, friendlyProductModel and friendlyProductModelStage. Optional pAppCode, mid, contentTargetModel and contentTargetId. |
The invoice-summary product labels are IN_CHANNEL_POINT_PRODUCT, CHANNEL_POINT_PRODUCT, COMMERCE_SYSTEM_INVOICE_PRODUCT, SCHEDULE_INVOICE_PRODUCT, P_APP_UNLOCK_PRODUCT, MEMBERSHIP_PRICE_PRODUCT, CHANNEL_ACTION_PRODUCT_TEMPLATE, MESSAGE_POLICY or UNKNOWN. Stage is CURRENT or LEGACY. Optional content target is SERIES, POST, SCHEDULE, MESSAGE_POLICY or UNKNOWN. Unknown classification remains unknown; it does not authorize a different operation.
Prices contain currency, amount and sourceRepresentation. Amount is always a decimal string. Source strings preserve their spelling and scale; they permit optional minus, 1–60 whole digits without leading zeroes except zero itself, and 1–30 fractional digits when a decimal point exists. String exponent notation is rejected. JSON numeric amount lexemes are captured before floating-point loss using Node 24's JSON reviver context, then expanded exactly from bounded exponent notation. Literal length is at most 256, absolute exponent at most 100, and the expanded result must fit the same decimal bounds. sourceRepresentation is decimal-string or json-number. No rounded Number becomes the monetary value.
Currency follows ^[A-Z][A-Z0-9_]{1,11}$. Different currencies and original/revenue/delegated/refunded categories remain separate; no summing, foreign exchange, inferred zero or reconciliation is performed. This numeric-lexeme extension applies only to commerce PAY bodies. Existing sales amounts still require strings, and provider fields retain their earlier projection.
Card/payment details, tokens, global buyer identifiers, raw gateway payloads and unknown nested fields are omitted. Names, nicknames, notes and reasons remain untrusted user content. Stored REFUNDED, withPg and isPgApplied are independent fields, not proof of a bank transfer, settlement eligibility or completed financial action.
## Core response limitations
Missing or null error remains permitted; false, zero, empty string, malformed values and objects reject as sanitized UPSTREAM_CONTRACT_MISMATCH. This guard applies to generic data-decoding callers, not body:none acknowledgements. Commerce's profile-lookup POST is explicitly read-only for uncertainty classification. The rejection is not optional enrichment, and it returns no records or confirmed financial result.
The cap includes ignored outer/nested fields; announced oversize rejects before reading, and absent, invalid or falsely small Content-Length does not disable streamed byte counting. The reader cancels/releases after success, rejection or stream failure. Oversize is sanitized as UPSTREAM_RESPONSE_TOO_LARGE; absent body, malformed JSON or stream failure is UPSTREAM_CONTRACT_MISMATCH. These failures cannot become optional missing enrichment. This opt-in cap does not change unrelated core callers; PAY and refund history retain their separate readers.
## Navigation and recovery
Outside demo, successful reads, buyer-selection guidance and CSV guidance select one console destination: /channels/{channelCode}/orders/subs-products, /orders/refunds, the corresponding path with the selected encoded oid/rid for detail, or /orders/download for CSV. The navigation wrapper requires the detail ID to agree with selectedRecordId and record identity. consoleUrl remains a compatibility alias of navigation.primaryUrl; the alias and sole links entry describe the same destination. List conditions follow the source URL contract described below. Authentication guidance and errors can return unavailable navigation; no account-home substitute is invented.
Success verification is confirmed:true with method repeated_source_commerce_read, atomicSnapshot:false, financialActionCompleted:false and transferVerified:false. Buyer-selection guidance is unconfirmed with financialRecordsRead:false; CSV guidance is unconfirmed with exportGenerated:false. writesPerformed:false excludes business writes; authorization and profile lookup still use POST.
| Error family | Agent response |
| --- | --- |
| INVALID_INPUT, COMMERCE_BUYER_NOT_OBSERVED | Correct the strict view or choose a profile returned by the same current search. |
| COMMERCE_CHANGED, channel/context/credential errors | Discard partial data and make a fresh authorized observation. |
| COMMERCE_RESPONSE_UNVERIFIED, COMMERCE_PAGE_MISMATCH, COMMERCE_FILTER_MISMATCH, COMMERCE_TARGET_MISMATCH | Do not invent empty records, broaden filters or substitute an ID. |
| UPSTREAM_RESPONSE_TOO_LARGE, UPSTREAM_CONTRACT_MISMATCH | Preserve the closed core observation; do not replace an oversized or contradictory response with absence. |
| COMMERCE_SEARCH_UNVERIFIED, COMMERCE_PROFILES_UNVERIFIED, COMMERCE_PROFILE_MISMATCH, COMMERCE_INVOICE_UNVERIFIED, COMMERCE_INVOICE_MISMATCH | Preserve the failed observation; absence and invalid data are different. |
| COMMERCE_AMOUNT_UNVERIFIED, PAY_RESPONSE_UNVERIFIED | Stop on unsupported precision or malformed source data; do not round it to fit. Missing numeric-source context is caught by the PAY JSON reader as PAY_RESPONSE_UNVERIFIED. |
| PAY_SERVICE_UNCONFIGURED / CONFIGURATION_INVALID, PAY_SERVICE_UNREACHABLE, PAY_RESPONSE_TOO_LARGE, PAY_AUTHORIZATION_UNVERIFIED, PAY_SERVICE_REJECTED | Operator configuration or source response needs attention; no automatic retry or silent handoff result. |
| AUTH_REQUIRED, PERMISSION_DENIED, CONNECTION_BUSY, UPSTREAM_HTTP_* | Resolve the connection/authorization/service condition without substituting another target. |
No job or task-resume path is added. Refund approval/rejection, amount entry, renewal cancellation, pass revocation and CSV generation remain human console work. Reread the appropriate operation after a person acts. The history view below supplies bounded video/download consumption only; it does not add generic pApp access or financial mutation.
## Refund consumption history
Only refunds_get accepts view {kind:"history",rid,type}, with type video or download. rid uses the same observed refund identifier and bounds as detail. Buyer, membership, pApp and content selectors are never caller inputs. The initial refund supplies its buyer and invoice; an observed channel profile plus invoice summary must produce a valid internal scope:
- distinctId is the channel profile identifier.
- A membership mid is sufficient by itself.
- Without mid, pAppCode and a POST or SERIES targetModel/targetId are required.
- targetModel and targetId must occur together; a target requires pAppCode. All supplied invoice fields must be compatible with this schema. Unsupported targets are not silently dropped to broaden the query.
A guest without a channel profile or other missing enrichment yields action_required / REFUND_HISTORY_SCOPE_REQUIRED. reason is buyer-profile-unavailable, invoice-summary-unavailable or invoice-scope-unsupported, in that priority. The result includes the observed refund and enrichment, historyRead:false, refundObserved:true and confirmed:false. It sends no history request. Authentication, invalid data and mismatches fail; they do not become scope guidance. Supported invoice scope, not friendly product label alone, controls availability.
For resolved scope, the channel-scoped core GET is /seller/api/v1/consume-histories/vod or /consume-histories/download, using the selected channel bearer and channel header. Query fields are only the derived distinctId, mid, pAppCode, targetModel and targetId. This endpoint uses a 30-second timeout, no redirect/cookies, and a separate streamed 2 MiB cap including unused properties. It requires HTTP 200 with a data object and absent/null error. HTTP 401/403 closes; oversized, malformed or inconsistent history is not optional absence. There is no API pagination or total.
For successful history, the bridge reads history twice, rereads the profile and invoice, rereads the refund and checks the final context. SERIES ambiguity may stop history reading early, but still requires the parent/context rechecks described below. Comparison is over projected records; dropped fields are not compared. It is sequential and not atomic. A success has code REFUND_HISTORY, record/selectedRecordId/view, buyerProfile, invoiceSummary, records, selection and coverage. selection contains type, profileDistinctId and the derived invoice filters. coverage is {returnedRecords: actual row count, maxRecords:300, complete:false, paginationAvailable:false}. No supplied/returned page cursor exists; an overlarge row set fails instead of being silently truncated.
Each row requires type VOD or DOWNLOAD matching the selected tab, positive safe postId, pAppCode and targetContent. targetContent contains posts ({id,title}), serieses ({id,title}) and stories ({id,postId}), each bounded to 300; titles are at most 4,096 characters. Optional row fields are seriesId, isIncludedInTargetMembership, isLocked, viewStartAt, viewEndAt and recordedAt. Timestamps are ISO datetimes with offsets; a present start/end pair must be ordered. Optional profileDistinctId and log identity references are checked against the derived buyer, then removed along with all logs. A selected pAppCode must match row.pAppCode, and POST requires row.postId to equal the target. SERIES requires one nested selected-target match and an absent/equal optional row.seriesId under the conservative rule below. Missing optional buyer references rely on the source endpoint's channel/buyer scope and are not an independent identity proof.
The success verification method is repeated_source_refund_history_read with confirmed:true, atomicSnapshot:false, financialActionCompleted:false and transferVerified:false. Raw logs, consumer/device/browser IDs, user agents and playback payloads never become output. Membership history may include content outside the membership. Neither inclusion/locked flags nor usage records decide refund eligibility, completion, entitlement or money. The tool never plays or downloads content and never calculates viewing duration or refund amounts.
REFUND_HISTORY_UNVERIFIED covers malformed history/envelopes/streaming or invalid viewing intervals; REFUND_HISTORY_TOO_LARGE covers its byte limit; REFUND_HISTORY_TARGET_MISMATCH covers wrong type/buyer/content; COMMERCE_CHANGED discards drift. Preserve these failures rather than turning them into an empty history. A missing optional JSON reviver source still affects commerce amounts separately. Success, scope guidance or SERIES guidance opens precisely /channels/{channelCode}/orders/refunds/{rid}/history/video-viewing-history or /history/download-history, tied to the selected refund and history type. No generic home fallback or automatic financial action follows.
## SERIES selection and field meaning
The bridge requires exactly one nested entry matching the target and permits row.seriesId only when absent or equal. Both video and download use this rule. With target 21 and all other guards valid:
| --- | --- | --- |
| Absent | Exactly one 21 | Success; add selectedSeries for 21 |
| 21 | Exactly one 21 | Success; preserve row.seriesId and add selectedSeries |
| Absent or 21 | One 21 plus unrelated IDs | Success; unrelated entries remain source metadata |
| 99 | Exactly one 21 | Unconfirmed SERIES guidance; withhold all rows |
| Absent, 21 or 99 | No 21, including an empty list | Unconfirmed SERIES guidance; withhold all rows |
| Absent or 21 | Two or more entries for 21 | Unconfirmed SERIES guidance; withhold all rows |
Successful SERIES rows add selectedSeries:{id,title,basis:"invoice_target_in_source_serieses"} from that single nested match. Original source fields remain intact except for the normal sensitive-field projection. No selectedSeries field is added for membership-only or POST scope. An empty source history can still return an empty observed list; it proves neither a selected series relationship nor complete absence of consumption.
Ambiguity returns action_required / REFUND_HISTORY_SERIES_UNVERIFIED with reason:series-relationship-unverified. It retains the observed refund and enrichments, selectedRecordId and view; selection includes only type, targetModel and targetId. verification is {confirmed:false,refundObserved:true,historyRead:true,historyRecordsReturned:false,financialActionCompleted:false,transferVerified:false}. There is no records or coverage property, no ambiguous-row dump and no valid subset.
A first-read ambiguity stops further history reads. If the second observation becomes ambiguous, all first-read rows are discarded. In either case the workflow rereads the profile, invoice and refund and checks final channel/operator/credential context before returning guidance. Drift still fails the operation. This handoff does not claim the two-history comparison used by success and does not start a task or retry automatically.
Before SERIES selection, the parser checks ordinary type/buyer/log/pApp/POST and viewing-time constraints across all rows in the observed response. An ambiguous row therefore cannot hide another row's ordinary mismatch or invalid interval. Other schema, authentication, byte-limit and service errors retain their failure behavior. Guidance opens the same exact selected-refund/type console tab; it does not broaden the buyer/content query or perform financial work.
The original SW screens select a displayed series title from the nested invoice-target match. The new rule deliberately limits which observations the bridge confirms; it does not redefine either source field, assert foreign-record disclosure or decide refund eligibility. Human inspection at the console is guidance, not proof of resolved backend semantics.
## List selection and browser limits
List `consoleView` states whether the address reproduces the requested selection.
Without member search, `exactMatch:true` preserves page, limit, optional order-number
search and the supported `isCP` or refund `status` filter. Orders use the source
`searchType=ORDER_NUMBER`; refunds use their source default order-number mode.
For member searches, the source console cannot restore the MCP-selected buyer:
its search saga selects the first profile, and the refund search mode is local UI
state. The bridge therefore supplies a broader parent list at page 1 / limit 300,
without search, while retaining `isCP` or `status`. `exactMatch:false`, `omitted`
and the parent relation/notice disclose this difference. Choose and verify the
member in the console; never present this parent as the same buyer-specific page.
Actual API queries and returned records keep the requested selection. Ambiguous
buyer guidance uses the same parent and stays unconfirmed without a PAY list read.
The wrapper requires matching selection, page, row identities, refund parent/status, next page, verification and console view. Inconsistent results or URLs have no navigation fallback. Existing empty out-of-range results retain their read policy without guaranteeing browser behavior. Detail, refund-history and monthly-download paths are unchanged. The person uses the console's own authentication.