diff --git a/restapi/restendpoints.md b/restapi/restendpoints.md index f641529..340f6a5 100644 --- a/restapi/restendpoints.md +++ b/restapi/restendpoints.md @@ -1188,6 +1188,126 @@ curl -X POST \ "https://cloud.handpoint.io/moto/reversal" ``` +## Reversal Operations Beta + +The Reversal endpoint allows you to **reverse any reversible transaction** (authorization, payment or refund) using only its identifier. The reversal is processed against the Handpoint gateway via Cloud API without requiring a physical payment terminal. + +Cloud API currently supports: + +- `POST /v1/reversal` — reverses a transaction identified by `originalGuid`. + +All request and response payloads are defined in [Reversal objects](restobjects#reversal). + +--- + +### /v1/reversal + +`Reversal` + +`POST /v1/reversal` is used to **reverse (void)** a previously completed transaction. The operation is linked to the original transaction via `originalGuid`. No card data is sent; the gateway resolves the original transaction internally. + +Reversals are typically used to cancel an authorization or payment shortly after it was processed, subject to acquirer rules. For partial reversals, provide `amount` and `currency` to release only part of the held funds. + +#### Parameters + +| Parameter | Notes | +| --------- | ----- | +| `Header: ApiKeyCloud` Required | Cloud API key used to authenticate the merchant. | +| `Request Body: ViscusReversalRequest` Required | [ViscusReversalRequest](restobjects#viscusReversalRequest) object identifying the transaction to reverse. | + +Typical fields (see [ViscusReversalRequest](restobjects#viscusReversalRequest) for full details): + +* `originalGuid` Required – GUID of the transaction to reverse. +* `messageReasonCode` Optional – ISO 8583 reason code. Defaults to `CUSTOMER_CANCELLATION` when not provided. +* `timestamp` Optional – Terminal timestamp in `YYYYMMDDHHmmssSSS` format. Defaults to the current server time when not provided. +* `amount` Optional – Amount to reverse, for partial reversals only (e.g. `"15.00"`). +* `currency` Optional – ISO 4217 3-character code, for partial reversals only (e.g. `"EUR"`). + +#### Returns + +| Result | Notes | +| ------ | ----- | +| `200` | Reversal accepted and processed. Response body is a parsed gateway object. | +| `400` | Business rule error from the gateway (for example, unknown `originalGuid` or reversal not allowed). Returned as `BadRequestError` with `error.code` and `error.details`. | +| `403` | Forbidden — the API key does not belong to a merchant, or the merchant is not configured. | +| `422` | Payload validation error (`VALIDATION_FAILED`) — `originalGuid` is missing, `messageReasonCode` is not one of the allowed values, or `currency` is not exactly 3 characters. | + +#### Behaviour examples + +* **Happy path – full reversal** + + ```shell + curl -X POST \ + -H "Content-Type: application/json" \ + -H "ApiKeyCloud: XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX" \ + -d '{ + "originalGuid": "0c9d9df0-48ec-11eb-81a1-470a19c80d3a" + }' \ + "https://cloud.handpoint.io/v1/reversal" + ``` + +* **Partial reversal** + + ```shell + curl -X POST \ + -H "Content-Type: application/json" \ + -H "ApiKeyCloud: XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX" \ + -d '{ + "originalGuid": "0c9d9df0-48ec-11eb-81a1-470a19c80d3a", + "amount": "15.00", + "currency": "EUR" + }' \ + "https://cloud.handpoint.io/v1/reversal" + ``` + +* **With explicit reason code** + + ```shell + curl -X POST \ + -H "Content-Type: application/json" \ + -H "ApiKeyCloud: XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX" \ + -d '{ + "originalGuid": "0c9d9df0-48ec-11eb-81a1-470a19c80d3a", + "messageReasonCode": "TIMEOUT_WAITING_FOR_RESPONSE" + }' \ + "https://cloud.handpoint.io/v1/reversal" + ``` + +* **Validation error – missing `originalGuid`** + + ```json + { + "error": { + "statusCode": 422, + "name": "UnprocessableEntityError", + "message": "The request body is invalid.", + "code": "VALIDATION_FAILED", + "details": [ + { + "path": "", + "code": "required", + "message": "must have required property 'originalGuid'", + "info": { "missingProperty": "originalGuid" } + } + ] + } + } + ``` + +#### Code example – full reversal + +```shell +curl -X POST \ + -H "Content-Type: application/json" \ + -H "ApiKeyCloud: XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX" \ + -d '{ + "originalGuid": "0c9d9df0-48ec-11eb-81a1-470a19c80d3a" + }' \ + "https://cloud.handpoint.io/v1/reversal" +``` + +--- + ## Batch Operations Beta :::info diff --git a/restapi/restobjects.md b/restapi/restobjects.md index a2125c9..5495b8a 100644 --- a/restapi/restobjects.md +++ b/restapi/restobjects.md @@ -1047,6 +1047,86 @@ The exact shape is very similar across these operations; some fields (such as `o } ``` +## Reversal {#reversal} + +### ViscusReversalRequest {#viscusReversalRequest} + +`ViscusReversalRequest` Object + +Object used by the [`POST /v1/reversal`](restendpoints#reversal-operations) endpoint to reverse any reversible transaction. Only `originalGuid` is required; all other fields are optional and default to sensible values when not provided. + +**Properties** + +| Property | Description | +| -------- | ----------- | +| `originalGuid` Required
*String* | GUID of the transaction to reverse (maximum 64 characters). | +| `messageReasonCode` Optional
*String* | ISO 8583 reason code for the reversal. Defaults to `CUSTOMER_CANCELLATION` when not provided. See [allowed values](#messageReasonCode) below. | +| `timestamp` Optional
*String* | Timestamp in `YYYYMMDDHHmmssSSS` format (17 characters). Defaults to the current server time when not provided. | +| `amount` Optional
*String* | Amount to reverse for partial reversals (e.g. `"15.00"`). When provided together with `currency`, only the specified amount is released from the hold. The most recent reversal received supersedes any earlier partial reversal. | +| `currency` Optional
[*Currency*](#currency) | ISO 4217 3-character currency code for partial reversals (e.g. `"EUR"`). Must be exactly 3 characters. | + +#### Allowed values for `messageReasonCode` {#messageReasonCode} + +| Value | Description | +| ----- | ----------- | +| `CUSTOMER_CANCELLATION` | Transaction cancelled by the customer. **Default when not provided.** | +| `UNSPECIFIED_NO_ACTION_TAKEN` | No action taken; reason unspecified. | +| `SUSPECTED_MALFUNCTION` | Terminal malfunction suspected. | +| `FORMAT_ERROR_NO_ACTION_TAKEN` | Format error with no action taken. | +| `COMPLETED_PARTIALLY` | Transaction completed only partially. | +| `ORIGINAL_AMOUNT_INCORRECT` | The original transaction amount was incorrect. | +| `RESPONSE_RECEIVED_TOO_LATE` | Response from the acquirer arrived after the timeout. | +| `CARD_ACCEPTOR_DEVICE_UNABLE_TO_COMPLETE_TRANSACTION` | The terminal was unable to complete the transaction. | +| `DEPOSIT_OUT_OF_BALANCE` | Deposit amount does not balance. | +| `NO_CHECK_IN_ENVELOPE` | No check found in the deposit envelope. | +| `PAYMENT_OUT_OF_BALANCE` | Payment amount does not balance. | +| `DEPOSIT_OUT_OF_BALANCE_APPLIED_CONTENTS` | Deposit out of balance after applying contents. | +| `PAYMENT_OUT_OF_BALANCE_APPLIED_CONTENTS` | Payment out of balance after applying contents. | +| `UNABLE_TO_DELIVER_MESSAGE_TO_POINT_OF_SERVICE` | Message could not be delivered to the point of service. | +| `SUSPECTED_MALFUNCTION_CARD_RETAINED` | Malfunction suspected; card was retained. | +| `SUSPECTED_MALFUNCTION_CARD_RETURNED` | Malfunction suspected; card was returned. | +| `SUSPECTED_MALFUNCTION_TRACK_3_NOT_UPDATED` | Malfunction suspected; track 3 was not updated. | +| `SUSPECTED_MALFUNCTION_NO_CASH_DISPENSED` | Malfunction suspected; no cash was dispensed. | +| `TIMED_OUT_AT_TAKING_MONEY_NO_CASH_DISPENSED` | Timed out while taking money; no cash dispensed. | +| `TIMED_OUT_AT_TAKING_CARD_CARD_RETAINED_AND_NO_CASH_DISPENSED` | Timed out taking card; card retained and no cash dispensed. | +| `INVALID_RESPONSE_NO_ACTION_TAKEN` | Invalid response received; no action taken. | +| `TIMEOUT_WAITING_FOR_RESPONSE` | Timed out waiting for a response from the acquirer. | +| `PREMATURE_CHIP_CARD_REMOVAL` | Chip card was removed before the transaction completed. | +| `CHIP_CARD_DECLINES_TRANSACTION` | Chip card declined the transaction. | +| `SIGNATURE_TIMEOUT` | Signature capture timed out. | +| `MERCHANT_REVERSAL_SIGNATURE_DECLINED` | Merchant-initiated reversal; signature was declined. | +| `NO_REVERSAL_REASON_CODE` | No specific reason code applies. | + +**Code example – full reversal (defaults)** + +```json +{ + "originalGuid": "0c9d9df0-48ec-11eb-81a1-470a19c80d3a" +} +``` + +**Code example – partial reversal** + +```json +{ + "originalGuid": "0c9d9df0-48ec-11eb-81a1-470a19c80d3a", + "amount": "15.00", + "currency": "EUR" +} +``` + +**Code example – with explicit reason code and timestamp** + +```json +{ + "originalGuid": "0c9d9df0-48ec-11eb-81a1-470a19c80d3a", + "messageReasonCode": "TIMEOUT_WAITING_FOR_RESPONSE", + "timestamp": "20240101120000000" +} +``` + +--- + ## Batch {#batch} ### BatchCloseRequest {#batchCloseRequest}