publ betaReference (MCP guide)
Open publ console

Orders and refunds screen contract

# Orders and refunds screen contract

Current implementation: orders_get and refunds_get provide bounded list/detail reads, member selection and a human monthly-download destination. Two tools and two partial workflows are added to the previously reviewed sales/provider checkpoint. The original checkpoint had 106 tools / 43 partial workflows; that was not functional completion. The follow-up history view reads bounded video/download events with derived scope.

The later current contract section makes the chosen behavior concrete. Existing navigation alone was not counted as implementation. Source comparison grants no live execution authority. This comparison uses the active Orders containers, OrderLists/OrderDetail, OrderRefundList/ OrderRefundDetail and OrdersDownload components, active orders/refund sagas, reducers and types, userProfiles and order repositories, ProfitableClient, API endpoint definitions and channel routes. Unused similarly named repository files are not evidence of the executed screen contract. No product or service request was executed for this analysis.

## Screen and request mapping

| Screen | Source composition | Exact console path after /channels/:channelCode |
| --- | --- | --- |
| Order list | ISSUER authorization, PAY GET /api/v1/orders with productModel ALL, then channel profile lookup for returned userId values | /orders/subs-products |
| Order detail | ISSUER authorization, PAY GET /api/v1/orders/:oid with productModel SubscriptionProduct, then channel profile and core invoice summary | /orders/subs-products/:oid |
| Refund list | ISSUER authorization, PAY GET /api/v1/refund-requests, then channel profiles for returned recipientId values | /orders/refunds |
| Refund detail | ISSUER authorization, PAY GET /api/v1/refund-requests/:rid, then channel profile and core invoice summary from the nested order | /orders/refunds/:rid |
| Refund consumption history | Core orderRefund history lookup using resolved profile/content target and source type | /orders/refunds/:rid/history/video-viewing-history or /download-history |
| Monthly CSV download | Orders/refunds list with isForCsv, source-selected past month, browser CSV generation | /orders/download |

All active Orders routes share OWNER or DELEGATEE with MANAGE_ORDER; CREATOR is rejected. MANAGE_MONETIZATION and MANAGE_SALES_OVERVIEW do not substitute. The obsolete channel-action-products component is not registered by the active Orders container.

The PAY wrapper is distinct from the core seller API. Authorization POST uses
the selected channel and merchant bearer. List/detail GETs use its resulting
ISSUER bearer and the private configured PAY apiKey. Order list uses ALL even
though the console path is subs-products; detail passes SubscriptionProduct as
the original wrapper does. Do not incorrectly filter list results to memberships
or replace this contract with an arbitrary generic request tool.

## Pagination, selection and profile enrichment

The active list controls offer limits 15, 30, 50, 100 and 300, with initial
page 1 / limit 300 (browser storage can retain another valid limit). Order list
supports order-number search and isCP boolean filtering. Refund list supports
ISSUED, REJECTED and REFUNDED, plus order-number search. PAY pagination exposes
currentPage, limit and totalEntries. Verify agreement, duplicates and page bounds;
one page must not claim complete history. Numeric database id, order oid and
refund rid are different identifiers. Detail paths use oid/rid, not the row id.

The source nickname/channel-member search first GETs user profiles with type
ALL, search, limit 500 and page 1, then silently takes profiles[0].user.distinctId.
Order search forwards that global user distinct ID as distinctId; refund search
forwards it as recipientId. The MCP must not silently choose a fuzzy first match.
Resolve an exact observed identity, or return bounded candidates for explicit
selection with truncation/ambiguity retained. A channel profile distinct ID is
not automatically interchangeable with a global user distinct ID.

Profile enrichment uses core endpoints with the selected channel bearer and
x-publ-channel-id:

- POST /seller/api/v2/channel-management/global-profiles/find-channel-profiles
  with userDistinctIds. This is a lookup POST, not a member mutation. The response
  is a dictionary keyed by global user distinct ID. Retain only requested keys
  and necessary profile identity/nickname fields; no demographic/profile dump.
- GET /seller/api/v2/channel-management/global-profiles/:userDistinctId/find-channel-profile
  for detail. Guest/missing-profile behavior must be explicit; an unavailable
  profile must not become a different buyer or fabricated empty account.
- GET /seller/api/v2/channel-management/profitable/system-invoices/:siid/summary
  for detail. Verify returned summary.siid against the observed nested invoice.
  The summary describes guest checkout, friendly model/stage and optional pApp,
  membership and content target. It neither grants installed-feature access nor
  authorizes revocation/refund.

The source detail saga opportunistically enriches profile and invoice data;
a stronger MCP result should explicitly distinguish observed, unavailable and
mismatched optional enrichment. It must never publish mismatched targets. Final
channel/operator/credential continuity and source parent/detail identity checks
are still required. Repeated observations are not an atomic snapshot.

## Projection and financial meaning

Use an explicit allowlist covering identifiers, source status, product/invoice
summary, observed prices, relevant dates, recurring/payment-method category and
minimum buyer/profile information needed by the screen. Treat titles, nicknames,
comments and reasons as untrusted text. Never expose raw cardNumber, billkey,
nonce, payToken, poqToken, paymentInfo, pgOriginal, pgHookOriginal or provider
failure payloads. No arbitrary nested subscription, issuer address/contact or
conductNote object should escape merely because it exists in an upstream DTO.

Amounts require special attention: orders/types.ts declares many Price.amount
fields as numbers, while delegated prices and receipt balances use strings.
The pricing-domain Price shared by the refund DTO accepts number | string. Sales-summary string-only parsing must not be
copied blindly into this workflow. The implementation must state which source
representations it supports, preserve values without arithmetic/rounding and
reject unrecoverable precision rather than claiming exactness after coercion.
Currencies can differ across delegated, original, discount, revenue and refunded
prices; do not require every amount to equal channel base currency or sum them.

Refund status, withPg and isPgApplied are distinct. REFUNDED alone must not be
reported as independently confirmed bank transfer. Preserve requested/refunded
and delegated amounts as separate observations. Missing refundedAmount is not
zero. A user-visible rejection reason is separate from a provider's raw error.

Approval/rejection, refund calculation/submission, renewal cancellation and pass
revocation remain human console actions. Hand off the verified record's exact
screen; no writer or token-bearing receipt URL is needed. A GET confirmation
must not claim that the bridge performed a refund or financial settlement.

## Distinct remaining screens

The download screen chooses a completed UTC month and calls lists with
isForCsv:true (orders limit 10000; refunds limit 1000) before local browser CSV
formatting. These larger exports are not ordinary pagination. Do not silently
implement them by reading every page or count them as complete with a list tool.
Any bounded export implementation or console handoff must be explicit.

Refund video/download consumption histories depend on source profile, membership
or content target, and installed-feature data. They require separate per-type
scope and projection review. Do not infer them from an order or refund status.
The bounded follow-up below now implements these two tab reads. Generic installed-feature
content management remains separately pending.

## Core response checks

A core response that carries a non-null error is refused even when it also carries data. This applies to member
search, list and single profile lookups, the invoice summary and channel context reads. An omitted or null error is
accepted, and error details are not projected. These four reads also stop at 2 MiB, counting every byte whether or
not a Content-Length is announced, and a size or body failure is reported as a failure, never as optional data.

## Current list destination

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.