Mark an unresolved payment approved or declined
Records by hand the final outcome of a payment the gateway never confirmed: approved or declined. Nothing is sent to the gateway and no money moves; the operator is telling Shuttle what the gateway did, having established it some other way, and Shuttle then applies everything that would have followed at the time. An approval attributes the money to the account's outstanding charges exactly as a successful payment does; a decline puts the contract into arrears and starts the retry schedule. Sent as a transaction update carrying status_override, which fixes the payment's gateway_status at UNRESOLVED.
Where you can do this
- Merchant View: Transaction (Mark as Declined / Mark as Approved)
- Merchant View: Transaction (Mark as Declined)
- Merchant View: Transaction (Mark as Approved)
Before you start
- The payment's
statusis UNRESOLVED or PENDING, or itsgateway_statusis UNRESOLVED or PENDING. - A payment a fraud workflow suspended (
gateway_statusUNRESOLVED whilestatusis PENDING) is closed to this function: its outcome is already recorded as approved and is waiting on the workflow. - The payment still has a contract, and to approve it that contract must still have at least the payment's amount outstanding on charges that can take an allocation (a charge in SETUP, ACTIVE, FAILING or ERROR), for the same account and the payment's legal entity.
- No instance option gates the function; the controls appear on the payment screen whenever the payment reads unresolved.
Rules
Checked by the API means Shuttle refuses the request however it is sent; a screen name means only that screen refuses it, and a direct API call would be accepted.
| Rule | Checked by | What you see |
|---|---|---|
The fields go inside a transaction object: {"transaction": {... }}. The record itself is identified by the id in the URL, not in the body | The API | VALIDATION_ERROR: "API body error: missing transaction object" |
status must be SUCCESS or DECLINED. A status override always sends the gateway response UNRESOLVED, and Shuttle accepts only those two against it. Anything else Shuttle cannot map is passed through unchanged and reaches Shuttle as no status at all, so it is refused the same way (the exception is a bare Shuttle status name, which survives the pass-through; it buys nothing, since the same two outcomes are all Shuttle will take) | The API | INVALID: "apiValidation.error.invalid" on field status |
Only a payment whose status is UNRESOLVED or PENDING, or whose gateway_status is UNRESOLVED or PENDING, may be overridden | The API | INVALID: "apiValidation.error.transaction.no.edit" |
A payment whose gateway_status is UNRESOLVED while its status is PENDING is excluded even though it meets the test above: that is an approval a post-submission fraud workflow suspended, and it may not be changed here | The API | INVALID: "apiValidation.error.transaction.no.edit" |
| Approving requires the contract to still have at least the payment's amount outstanding, summed over the charges that can take an allocation, counting only the parts belonging to the payment's account and legal entity. Declining is not checked against anything | The API | INVALID (BOLT-1169) |
| For a capture the same check is made against the parent authorisation's remaining capturable amount instead of the contract's charges | The API | INVALID (BOLT-1169) |
The payment's gateway_status reads UNRESOLVED after an override. By design: Shuttle sets it to UNRESOLVED on this path rather than carrying anything over, so that a reader can tell the recorded outcome is an operator's and not the provider's. It is set, not preserved — a payment admitted because its gateway_status was PENDING loses that PENDING | The API | (none; the recorded status is the operator's, the gateway status is stamped UNRESOLVED) |
| Merchant View confirms first ("Are you sure you wish to mark this transaction as approved?" / "...as declined?") and, on any refusal, rolls the record back and shows one generic message, so the operator never learns which rule rejected the change | Merchant View screen | "Sorry, an error has occurred please try again later." |
What happens
- The caller receives the updated payment:
statusSUCCESS or DECLINED,processedfilled in with the time of the override when it was empty, andupdated_sessionrecording who made the change. The gateway is not contacted and no money moves. - Approved: the payer is sent the payment notification the client's notification connectors are configured to send for a successful payment, the same one a live payment would have triggered. The operator is not warned of this before confirming.
- Declined: a contract that was ACTIVE, FAILING, PENDING or in error moves to FAILING and its failure count for the account rises; on a recurring contract, or a one-off still owing, the next payment is re-derived from the client's retry schedule, or retries are marked exhausted when the schedule is spent. The saved payment method moves to FAILING and its stored details are discarded. PAYMENT.FAILED, CONTRACT.UPDATE and ACCOUNT.UPDATE follow, and a payment link becomes usable again. The payer is not notified.
- Either way the payment's
gateway_statusis stamped UNRESOLVED, and is meant to be: an override is an operator's decision, so lists, exports and reports go on showing that the outcome did not come from the provider. The value is written rather than left alone, so a payment admitted on a PENDING gateway status no longer reports it afterwards. A consequence of the same design is that the payment can be marked the other way later, and the second marking does not undo the first. - No scheduled job reconciles this against the gateway afterwards, so a wrong outcome generally stays wrong until someone notices. For gateways that send one, a later notification from the provider can still resolve a payment that was declined by hand: see the processor's own page for which do.
Who can do this
- Roles: Admin only. Support holds none of the keys Shuttle accepts, and status overrides are deliberately withheld from Support.
- Permission keys (for clients managing permissions directly): any one of
agreement,payment_contract,payment_transactionor*, held on the client or on the payment's division; Admin's*satisfies it. Without one the caller sees UNAUTHORIZED (HTTP 401), "Missing required permissions [...] on [...]". The check also passes, with no permission key at all, when the request carries a session bound to the payment's own account, which is the payer's session rather than an operator's. Separately, a caller whose session is bound to a different account is refused with NO_PERMISSION (HTTP 403), "No permissions". - Merchant View requires
payment_transactionorpayment_transaction_viewon the client or a division to open the payment screen, andpayment_transaction(or*) on any object at all to show the two controls. - No instance option gates this function, and no app option hides it.
Related
Updated 34 minutes ago