ticket_refund_settings_apply — save cancellation settings
Save the exact installation-default cancellation settings prepared by ticket_refund_settings_get. Use usage to enable/disable the existing policy, or rules to edit selected existing fee/period fields. This changes cancellation fee configuration, not money to refund. It does not execute refunds, cancellations, payments or settlement, create/delete/reorder rows, edit purchase-time rules or rewrite event-specific policy copies.
See the workflow.
Inputs and authorization
| Field | Required here | Contract |
|---|---|---|
channelCode |
Yes | string; min length 1; max length 512; The exact channelCode selected through channel discovery. Do not supply a URL, channel numeric ID, account ID or credentials. |
pAppCode |
Yes | E00003 |
refundPolicyId |
Yes | string; min length 1; max length 160; pattern ^[A-Za-z0-9_-]+$; Exact resource identifier returned by this operation, never a title, unrelated row ID or URL. |
operation |
Yes | one of: usage, rules (by kind) |
operation.isUsed |
Yes | When operation.kind is usage: boolean |
operation.edits |
Yes | When operation.kind is rules: array; min items 1; max items 100 |
operation.edits[].refundPolicyItemId |
Yes | When operation.kind is rules: string; min length 1; max length 160; pattern ^[A-Za-z0-9_-]+$; Exact resource identifier returned by this operation, never a title, unrelated row ID or URL. |
operation.edits[].dayBoundary |
No | When operation.kind is rules: integer; minimum 0; maximum 14 |
operation.edits[].fee |
No | When operation.kind is rules: one of: none, fixed, percentage (by kind) |
operation.edits[].fee.amount |
Yes | When operation.kind is rules and operation.edits[].fee.kind is fixed: string; pattern ^(0|[1-9]\d{0,9})(\.\d{1,2})?$ |
operation.edits[].fee.percent |
Yes | When operation.kind is rules and operation.edits[].fee.kind is percentage: string; pattern ^(0|[1-9]\d{0,9})(\.\d{1,2})?$ |
requestId |
Yes | string; pattern ^[A-Za-z0-9_-]{8,80}$; Reuse this requestId and the exact original input after interruption. Never change requestId to retry an uncertain write. |
expectedRevision |
Yes | string; pattern ^[a-f0-9]{64}$; Exact revision returned by the matching read for this channel, installed pApp and resource. |
dryRun |
No | boolean; default false |
Only the selected channel's one exact E00003 installation is allowed. OWNER is permitted; a DELEGATEE needs MANAGE_P_APPS and current manual policy proving it is not a content-only channel. Connection, credential version, actor, installation, consumer, currency and authorization are bound and rechecked. Account scope and foreign channel resources are excluded. BOOK_AND_TICKET_V3 uses the selected API origin with no shop-store lookup or BCS fallback.
All input/operation objects are strict. Business IDs use 1–160 ASCII letters, digits, underscores or hyphens. Currency, raw rows, consumer, URLs, credentials and event/booking targets are not caller inputs. Read the period and fee rules before preparing an edit. Rules require the policy enabled; enabling it and editing rules are separate prepared operations.
Plan examples
Plan disabling an observed existing policy:
{
"channelCode": "L2NoYW5uZWxzLzk4",
"pAppCode": "E00003",
"refundPolicyId": "refund-policy-example",
"operation": {"kind": "usage", "isUsed": false},
"requestId": "refund-usage-example-001",
"expectedRevision": "0000000000000000000000000000000000000000000000000000000000000000",
"dryRun": true
}
Independently, plan the percentage-fee operation prepared by the getter on an enabled policy:
{
"channelCode": "L2NoYW5uZWxzLzk4",
"pAppCode": "E00003",
"refundPolicyId": "refund-policy-example",
"operation": {
"kind": "rules",
"edits": [
{"refundPolicyItemId": "refund-rule-example", "fee": {"kind": "percentage", "percent": "12.34"}}
]
},
"requestId": "refund-rules-example-001",
"expectedRevision": "0000000000000000000000000000000000000000000000000000000000000000",
"dryRun": true
}
Replace the illustrative IDs and all-zero revision with current authorized observations and the exact operation preparation. These examples are alternatives, not a disable-then-edit sequence. After inspecting a new authorized plan, repeat the same input with dryRun:false. A plain discovery revision or a revision for different settings is not valid preparation.
Effects and readback
Usage performs one PATCH to /seller/api/v3/refund-policy/{refundPolicyId}/enabled or /disabled with an empty object body. Only the policy's isUsed changes; all observed rows and other returned fields are preserved.
Rules perform one POST to /seller/api/v3/refund-policy/{refundPolicyId} with the complete observed refundPolicyItems array. The adapter constructs that body; callers submit only selected edits. Row identities, order, individual usage flags, purchase-time rows, unrelated fields and unknown returned data are preserved, except explicitly edited fields and the required next-row maximum adjustment. Choosing a fee type resets its unused amount/percentage fields to zero. Selecting zero percent remains PERCENTAGE, distinct from NONE.
The physical shared boundary accepts no extra query parameters, outer body fields, alternative service/header mode or mismatched parent/consumer. The ticket transport expects HTTP 200/data without the shop-specific success/action envelope. Generated insertedAt/updatedAt timestamps are ignored. No atomic snapshot, backend compare-and-swap or causal attribution is promised.
Results and recovery
| Result | Interpretation |
|---|---|
COMMERCE_OPERATION_PLAN |
dryRun:true, writesPerformed:false. A new plan creates no journal. An existing-task dry run may report historical state. |
COMMERCE_OPERATION_COMPLETE |
phase:done after matching readback or verified no-op; noop:true means no save was dispatched. Current completion has verification.confirmed:true, with atomicSnapshot/causalityVerified:false. |
COMMERCE_OPERATION_PENDING |
Sent settings remain uncertain. Inspect lastCode, phase, saveAcknowledged and journalPersistence. Partial saves and preservation mismatches do not complete. |
COMMERCE_JOURNAL_PERSISTENCE_FAILED |
Saved progress is uncertain; preserve the task and original request rather than starting again with a new ID. |
The task namespace is commerce-ticket-refund-settings, separate from commerce-ticket-pricing. Its phases are prepared, sent and done. writesPerformed describes the saved task: false for prepared/no-op, true after acknowledgement, null for an unacknowledged sent effect. It is not evidence of a write by the current status/resume call.
task_status reports historical saved progress after current context checks. Shared task_resume is observation-only, with a guard against business writes. Known-unsent prepared work may continue only through the identical original apply after revalidation; the later policy guard makes a prepared task with its own durable claim observation-only. Once sent, neither resume nor identical apply dispatches another save. These edits need no created-resource receipt: exact-policy observation may resolve a lost acknowledgement when the full requested state and preservation checks match. That does not prove causality.
Never change requestId or infer success from an unchanged console after an uncertain save. COMMERCE_CONFLICT or changed settings before dispatch require resolving stale preparation; REQUEST_ID_CONFLICT means the original input was changed. Context/credential/installation changes cannot adopt an old journal. A partial save, refused/unconfirmed effect or unrelated field change remains pending unless valid observation resolves it.
Completed apply replay, status and completed resume are historical with current verification false, writesPerformedThisCall:false and no current verified link. Original completed apply/resume may finish releasing its own claim after an earlier release failure; historical status alone cannot do so or release another task's claim. Use a fresh getter for current values. There is no automatic rollback. A restoration attempt is a separate authorized reverse edit: reread the current policy, prepare the exact reverse operation, then independently verify it.
Console destination
Fresh validated discovery/usage results use /channels/{channelCode}/p-apps/E00003/settings. Rule preparations, plans, no-ops and current confirmed rule completions use /channels/{channelCode}/p-apps/E00003/settings/edit-refund-policy. Both use screen channel.ticket-refund-settings; the host retains its validated operation-derived refundSettingsView of usage or rules. This is output routing metadata, not a caller selector or URL argument.
The host checks policy/row relationships and the exact current request and screen before returning one configured-console link. Pending, error and historical results have no current verified destination. Use the returned URL, not a reconstructed host. A dry-run link or console visit alone does not prove an MCP change.
Shared policy admission and recovery
They correct admission to the existing cancellation-settings workflow without changing its inputs, transport or task type.
Rules and usage use the same whole-policy claim, including different selected rule IDs, inverse values and fresh all-matching no-ops. The rules POST replaces the full row set; individual rows are not separate lock resources. A new requestId, revised preparation or refreshed credential cannot bypass another pending task for that policy. TICKET_REFUND_SETTINGS_PENDING means preserve and observe the original task before another change. A malformed or mismatched claim returns TICKET_REFUND_SETTINGS_GUARD_UNVERIFIED and does not authorize deletion or replacement of that evidence.
The canonical commerce-ticket-refund-settings store creates a pending-policy-guard child through JobStore.child, preserving its filesystem or hosted backend/grant namespace. The key binds version, environment, verified consumerId and refundPolicyId; ownership binds original taskId/fingerprint. There is no directory-scanning fallback, caller-selected namespace, automatic expiry or global cross-client lock. Same-policy execution serializes, while a different verified policy resource can progress independently.
A fresh no-op must check this policy claim under the execution lock, reread the stable snapshot and confirm the reconstructed request is still null before persisting its own completed journal. It writes no target claim and cannot release an existing one. A rejected no-op creates no completed journal. A rejected changed request may retain its own prepared/unsent journal, but cannot replace the original journal, snapshot or claim. Successful get/dryRun is not admission to a later real apply.
Persistence boundaries
TICKET_EDIT_GUARD_PERSISTENCE_FAILED is the reused persistence error code for this cancellation-policy guard; it does not mean a schedule-edit operation ran. COMMERCE_JOURNAL_PERSISTENCE_FAILED separately marks uncertain journal storage. Preserve journalPersistence, lastCode, phase and the original identity. Do not infer durable release from a response error or a visible done phase, and do not remove claims/locks to make another request succeed.
Original-task observation and release
Shared task_resume always rejects business writes. For phase:prepared it returns historical saved status without a fresh policy read or claim enrollment/release. Identical original apply may continue known-unsent work after revalidation, but a prepared journal with its own durable claim is converted to sent observation without PATCH/POST. Once sent, original apply/resume reads the exact policy without repeating either operation.
Two stable observations must match requested and preserved business fields under the existing timestamp normalization, and completion must be saved before releasing the original claim. A 200 acknowledgement, unchanged old value, dry run, status read or elapsed time cannot release it. Historical completed status is not a fresh full-policy check. Completed original apply/resume can finish its own interrupted release but cannot release another task's claim or assert current-state verification. After resolution, a fresh operation still obeys normal constraints, including enabled-policy requirements for rules.
Pre-upgrade sent journals with no claim are enrolled only by their original sent apply/resume recovery, preserving input, context, before, fingerprint and requestDigest. Status does not enroll them. There is no automatic inventory or migration, so rollout remains on HOLD pending separate inspection/settlement of older work. Other grants/client namespaces, external callers, manual edits, event-policy copies and other commerce workflows are not coordinated; changing grants does not prove previous uncertainty resolved.
The public methods, operation shapes and physical GET/PATCH/POST routes are unchanged. This guard does not add cancellation, refund, payment, settlement or PG execution. Restoration remains a fresh explicit configuration change after uncertainty is settled, with independent readback; it does not reverse bookings, copied event policies or charges. Actual service/original-console/restoration/PG/LLM/hosted verification, translation backlog and D-PLAN-1 remain separate.