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

Before you start

  • The payment's status is UNRESOLVED or PENDING, or its gateway_status is UNRESOLVED or PENDING.
  • A payment a fraud workflow suspended (gateway_status UNRESOLVED while status is 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.

RuleChecked byWhat you see
The fields go inside a transaction object: {"transaction": {... }}. The record itself is identified by the id in the URL, not in the bodyThe APIVALIDATION_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 APIINVALID: "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 overriddenThe APIINVALID: "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 hereThe APIINVALID: "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 anythingThe APIINVALID (BOLT-1169)
For a capture the same check is made against the parent authorisation's remaining capturable amount instead of the contract's chargesThe APIINVALID (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 PENDINGThe 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 changeMerchant View screen"Sorry, an error has occurred please try again later."

What happens

  • The caller receives the updated payment: status SUCCESS or DECLINED, processed filled in with the time of the override when it was empty, and updated_session recording 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_status is 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_transaction or *, 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_transaction or payment_transaction_view on the client or a division to open the payment screen, and payment_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


Did this page help you?