Docs Cloud Refund Operations

Cloud refund operations

Mockarty Cloud can return all or part of a settled provider payment without inventing a successful result. The refund is reserved before the provider call and is bound to the exact invoice, order, payment, Space, provider account, connector version, currency, amount, and idempotency key. A license or entitlement changes only after authenticated provider evidence proves that the refund completed. A bonus-paid Team purchase has no provider payment: it supports an exact full refund back to the original promotional lots instead, with each lot’s original expiry and no new validity period.

Cancelling instead of a refund

The cabinet has no refund button. To stop paying, cancel the plan: it stays active until the end of the period you already paid for and then moves to the free plan. The unused part of that period is not returned to the card.

If you were charged by mistake, or a payment went wrong, contact support. Support checks the payment and can return it; everything below describes how that return is carried out.

When a return is being processed, the invoice shows it as processing until the payment provider confirms the result. For a current Team period, Mockarty first makes the shared Space read-only and waits for the shared server to confirm that change; the return remains pending and no card or promotional balance is moved while that server is unavailable. A definitive refusal by the provider leaves the invoice as it was; normal access to a still-paid Team period returns after the shared server confirms the restoration. Provider capabilities differ: a partial return that the provider cannot make is refused, never silently turned into a full one.

A duplicate payment is refunded automatically

When a provider confirms a payment late — after Mockarty has already closed that checkout and the same subscription period has been paid by another payment — the late payment is a duplicate. It is never applied a second time: the plan stays with the payment that activated it, the duplicate order moves to refunded, its invoice stays void, and the whole amount goes back to the original payment method with the reason duplicate_settlement. No operator action is needed; the refund appears in the invoice history like any other, and it settles once the provider confirms it.

A duplicate refund is not held back by a spend hold. If the account’s own payment attempts raised a hold in Reviews and appeals, further payments wait for the review, but money the system returns is released regardless — the hold protects against spending, not against getting a duplicate charge back.

Recover an ambiguous payment

If a checkout or renewal crossed the provider boundary but Mockarty cannot prove its outcome, support sees an operator_required payment incident. A dedicated operator token with the exact operator:commerce:write scope can read only these redacted incidents:

curl -fsS 'https://cloud.mockarty.ru/api/v1/cloud/operator/payment-incidents' \
  -H "Authorization: Bearer $MOCKARTY_TOKEN"

Verify the payment in the provider cabinet and use the returned operation ID and generation. Only two decisions are accepted:

  • reject: authoritative provider evidence proves that no charge was created.
  • retry: the pinned provider supports exact replay or authoritative readback for this payment.

There is no manual succeeded action. A retry returns the same frozen operation to bounded provider recovery; only authenticated provider evidence can activate or renew a subscription. Rejection cancels the frozen order, voids its open invoice, and releases the matching risk reservation atomically.

curl -fsS -X POST \
  'https://cloud.mockarty.ru/api/v1/cloud/operator/payments/PAYMENT_OPERATION_ID/resolve' \
  -H "Authorization: Bearer $MOCKARTY_TOKEN" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: payment-resolution:SUP-1042:g7' \
  --data '{"action":"retry","reason_code":"provider_readback_confirmed","generation":7}'

Reuse the same idempotency key after a lost response. 409 stale_generation means the incident changed and must be read again. 409 payment_recovery_unsupported means this pinned provider cannot recover this exact effect safely; do not create a new payment to work around it.

Resolve an operator incident

When automatic recovery cannot prove the outcome, support receives an operator_required refund incident. Use a dedicated Cloud operator API token with the exact operator:commerce:write scope. Operator routes are excluded from automatically generated MCP tools.

The same scope can read the redacted incident list and resolve an incident. Start with:

mockarty-cli --server 'https://cloud.mockarty.ru' --token "$MOCKARTY_TOKEN" cloud-refunds list

The command reads the refunds array returned by the refund-only GET /api/v1/cloud/operator/refunds route. The same token cannot read the broader cross-tenant payment ledger. Verify the provider record and the returned generation, then choose only one of these actions:

  • reject: provider evidence proves that no refund was applied; Mockarty releases the reserved risk capacity.
  • retry: the pinned provider supports authoritative readback or exact replay; Mockarty reopens bounded recovery.

An operator cannot enter a successful money result manually.

mockarty-cli --server 'https://cloud.mockarty.ru' --token "$MOCKARTY_TOKEN" \
  cloud-refunds resolve REFUND_OPERATION_ID \
  --action retry \
  --generation 4 \
  --reason-code provider_readback_confirmed \
  --idempotency-key refund-resolution:SUP-1042:g4

The idempotency key identifies this exact decision and must be reused unchanged after a lost response. A changed action, reason, or generation needs a new key. 409 stale_generation means another worker or operator already changed the incident; read it again. 409 refund_resolution_conflict means the requested decision contradicts the durable provider or risk authority. Every incident row carries provider_status, the provider’s own last word: succeeded means the money already left and retry lets the ledger catch up; canceled means reject. A rejected refund sends the customer the “Refund could not be completed” letter and asks them to contact support; a rejected payment sends “Payment not completed” for a one-off order, or “We could not renew your plan” for a renewal.

SDK equivalents are CloudRefunds().ListRefunds / ResolveRefund in Go, client.cloud_refunds.list_refunds / resolve_refund in Python, and client.cloudRefunds().listRefunds / resolveRefund in Java.

Successful settlement also creates an immutable fiscal-correction intent. Delivery to the configured fiscal provider is asynchronous; the existence of the intent is not proof that a correction receipt has already been delivered.

Resolve a fiscal receipt incident

A receipt that the cash-register provider could not issue or confirm waits for an operator under Money incidents as a Receipt row (also GET /api/v1/cloud/operator/fiscal-intents, one row’s detail at /fiscal-intents/{id}). Money already moved; the receipt is compliance debt, not a payment failure. Each row carries reason, and the console shows the same label:

reason What happened What to do
terminal_readback the receipt was issued but the provider cannot confirm it check the receipt with the provider, then attach the verified receipt
retry_exhausted, provider_rejected, receipt_status, dispatch_ambiguous, claim_authority issuing kept failing, or the outcome is unknown reissue the receipt once you know none was issued
contact_unavailable, connector_unavailable, route_unresolved, authority_unavailable, request_sealing_failed the receipt could not even be prepared (no payer contact, no cash-register connector, no fiscal route) fix the cause (connector, seller route), then rebind the authority

Every decision is step-up confirmed (fiscal_operator_resolution), keyed by Idempotency-Key, and recorded against the row’s generation:

# receipt exists at the provider — attach it
curl -X POST https://cloud.example.com/api/v1/cloud/operator/fiscal-intents/INTENT_ID/resolve \
  -H "Authorization: Bearer $OPERATOR_TOKEN" -H "Idempotency-Key: fiscal-attach-INTENT_ID" \
  --data '{"action":"attach_verified_receipt","reason_code":"receipt_recovered","evidence_kind":"provider_readback","evidence_digest":"<sha256 of the provider readback>","receipt_reference":"<provider receipt uuid>","generation":13}'

# no receipt was issued — issue it again
curl ... --data '{"action":"reissue_receipt","reason_code":"receipt_reissued","evidence_kind":"operator_attestation","evidence_digest":"<sha256 of your attestation>","generation":13}'

# the receipt could not be prepared — prepare it again after the cause is fixed
curl ... --data '{"action":"rebind_authority","reason_code":"authority_restored","evidence_kind":"operator_attestation","evidence_digest":"<sha256 of your attestation>","generation":0}'

A stale generation answers 409 fiscal_resolution_conflict; a rebind on a row that is not authority-blocked, or a reissue on a blocked one, answers the same 409 and names the state the decision serves. Every resolution opens a new attempt budget and a fresh 24-hour deadline for the worker. When the receipt is confirmed the customer receives it by e-mail.

Retry a failed renewal

When the saved payment method is refused for good, the renewal cycle ends as failed. Nothing is charged; paid features remain available only until the paid-through date, not through the payment grace period. The customer receives the “We could not renew your plan” letter (and the same in-app notice) naming that date and asking to update the payment method. If the paid-through date passes without renewal, a separate payment-needed notice names the grace deadline. A cycle also reaches the queue as operator required when the risk authority held the charge or when its risk reservation can no longer be resumed. Either way the cycle stays visible to operators under Money incidents as a Renewal row (also GET /api/v1/cloud/operator/renewal-incidents).

Each row names its reason, and the console shows the same label next to the provider:

reason What happened
provider_refused the saved payment method declined the charge
provider_unanswered the provider never confirmed or refused the charge
risk_held the risk review held the charge for an operator
risk_reservation_dead the attempt’s risk reservation can no longer be resumed; a retry opens a new one
provider_not_recurring the pinned provider cannot charge a saved method

Retry puts the cycle back on the schedule for a fresh attempt with its own risk reservation. It is accepted only when the linked payment incident is closed, the subscription is still active and not cancelled, and the row’s generation matches what you loaded — otherwise the console answers 409 and asks you to reload. A cycle whose period a later cycle has already paid cannot be retried at all: the API answers 409 renewal_period_superseded, and the next sweep closes the cycle. The recurring worker then charges the saved method again within its next cycle.

curl -X POST https://cloud.example.com/api/v1/cloud/operator/renewals/RENEWAL_ID/retry \
  -H "Authorization: Bearer $OPERATOR_TOKEN" -H "Idempotency-Key: renewal-retry-20260905-1" \
  -H "Content-Type: application/json" \
  -d '{"generation": 4, "reason_code": "provider_recovery_retry"}'

The operator MCP exposes the same queue as cloud_renewal_incidents_list.

Check daily commerce reconciliation

Open Operator console → Commerce reconciliation to compare three independent records: the payment provider’s authoritative readback, fiscal receipt delivery, and Mockarty’s accounting projection. A run remains Checking providers until every scheduled provider readback reaches a terminal result. Partial provider evidence means at least one provider could not be checked; it must not be treated as a clean run.

Findings contain stable entity references, amounts, currencies, and one-way evidence digests. They never contain customer identity, connector secrets, fiscal request bodies, or raw provider responses. An open finding closes automatically only after a later reconciliation proves that the discrepancy has healed. Operators cannot manually declare a payment or refund successful from this screen.

For read-only support automation, a Cloud operator may call GET /api/v1/cloud/operator/commerce/reconciliation/runs and GET /api/v1/cloud/operator/commerce/reconciliation/findings. Optional status is open or resolved; optional severity is warning or critical; limit is from 1 to 500. These endpoints require Cloud operator access and are not available through the customer SDKs, CLI or MCP tools.