Handling Underpayments and Overpayments

This documentation guides seamlessly managing underpayment and overpayment through the Refund Feature in the Merchant Dashboard. The feature allows you to identify discrepancies in incoming transactions and initiate reversals effortlessly.

To understand the functionality, you can watch the demo video.

Handling on the API Level

Upon fulfilment of a transaction, a webhook response will be sent containing the expected amount (expectedAmount), the amount received (amountReceived) and the variance type (varianceType) which describes if the transaction is an overpayment or an underpayment.

For transactions fulfilled via bank transfer, identified by the field "type":"bank_transfer", it is very essential to verify if the transaction was marked as an underpayment or overpayment.

If the varianceType is marked as underpayment or overpayment, you can go ahead to refund the paying customer depending on your business logic, via the refunds API. You can also process refunds via your dashboard.

Note: You would also receive "INCORRECT_AMOUNT" within the message field for such transactions.

Fields to look out for:

FieldDescription
Amount ReceivedClearly indicates the actual payment received.
Amount ExpectedHighlights the expected payment amount for easy comparison.
Variance TypeIdentifies if the transaction involves an overpayment or an underpayment, providing clarity on the nature of the issue
Action RequiredClearly specifies the recommended action for the merchant, either through the dashboard or the Refund API.
MessageProvides a descriptive message to inform the merchant about the nature of the discrepancy, such as "Incorrect amount received."

Example
In the event of an underpayment, the status will still be marked as "success." However, there will be a noticeable difference between the "expectedAmount" and the "amountReceived."

{
  "event": "collection.successful",
  "data": {
    "business": "63ea1655a258d844ff26eed3",
    "virtualAccount": "65804647acb578b2ebf9dd60",
    "sessionId": "881100188872888456991",
    "senderAccountName": "Ahmed Bayero",
    "senderAccountNumber": "7821444125",
    "senderBankName": "gtb",
    "sourceCurrency": "NGN",
    "destinationCurrency": "NGN",
    "sourceAmount": 90,
    "destinationAmount": 90,
    "description": "Test under",
    "amountReceived": 88.6455,
    "fee": 1.2599999999999998,
    "customerName": "Ahmed Bayero",
    "settlementDestination": "wallet",
    "status": "successful",
    "initiatedAt": "2023-12-18T13:19:12.177Z",
    "createdAt": "2023-12-18T13:19:12.349Z",
    "updatedAt": "2023-12-18T13:19:12.349Z",
    "reference": "b9ce8ac8-ba7a-4ea8-89f5-1f4f29dc55a2",
    "amountExpected": 100,
    "varianceType": "underpayment",
    "actionRequired": "Process refund via dashboard/Refund API",
    "message": "INCORRECT_AMOUNT"
  }
}
{
  "event": "charge.successful",
  "data": {
    "chargeReference": "fcr-bt-63382e717549d3171",
    "amountToSettle": 109.32945,
    "id": 12152,
    "authorization": {
      "mode": null,
      "redirect": null,
      "metadata": null
    },
    "auth_model": null,
    "amount": 100,
    "amountExpected": 100,
    "amountReceived": 111,
    "varianceType": "overpayment",
    "currency": "NGN",
    "fee": 1.67,
    "vat": 0.12,
    "message": "INCORRECT_AMOUNT",
    "actionRequired": "Process refund via dashboard/Refund API",
    "status": "success",
    "reference": "AA148328110983",
    "description": "",
    "type": "bank_transfer",
    "customer": {
      "name": "Joy Awili",
      "email": "[email protected]",
      "phoneNumber": null
    },
    "metadata": {},
    "settlementDestination": "wallet",
    "virtualAccount": {
      "bankName": "wema",
      "id": "658047ad35064b4deac51498",
      "bankCode": "035",
      "accountName": "FIN-AL-BARNAWI, Khalid",
      "accountNumber": "7945158574",
      "sessionId": "8811001203472555456991",
      "channelName": "wema",
      "payerAccountNumber": "7821444125",
      "payerAccountName": "Ahmed Bayero",
      "payerBankName": "gtb",
      "payerBankCode": "058",
      "expiresAt": "2023-12-18T13:42:54.000Z",
      "business": "63ea1655a258d844ff26eed3"
    }
  }
}
{
  "event": "charge.successful",
  "data": {
    "chargeReference": "fcr-bt-038d76c6f4c28d040",
    "amountToSettle": 98.495,
    "id": 12164,
    "authorization": {
      "mode": null,
      "redirect": null,
      "metadata": null
    },
    "auth_model": null,
    "amount": 120,
    "amountExpected": 120,
    "amountReceived": 100,
    "varianceType": "underpayment",
    "currency": "NGN",
    "fee": 1.5,
    "vat": 0.1,
    "message": "INCORRECT_AMOUNT",
    "actionRequired": "Process refund via dashboard/Refund API",
    "status": "success",
    "reference": "AAC148328110983",
    "description": "",
    "type": "bank_transfer",
    "customer": {
      "name": "Joy Awili",
      "email": "[email protected]",
      "phoneNumber": null
    },
    "metadata": {},
    "settlementDestination": "wallet",
    "virtualAccount": {
      "bankName": "wema",
      "id": "658062a735064b3ff6c514b2",
      "bankCode": "035",
      "accountName": "FIN-AL-BARNAWI, Khalid",
      "accountNumber": "7947438942",
      "sessionId": "8811001203148500456991",
      "channelName": "wema",
      "payerAccountNumber": "7821444125",
      "payerAccountName": "Ahmed Bayero",
      "payerBankName": "gtb",
      "payerBankCode": "058",
      "expiresAt": "2023-12-18T15:37:59.000Z",
      "business": "63ea1655a258d844ff26eed3"
    }
  }
}

The response gotten when the transaction status is re-queried:

{
    "status": true,
    "message": "Payment details fetched",
    "data": {
        "id": 3001,
        "businessId": "60c1d7e39d9fc6b87dedb04e",
        "paymentLinkId": null,
        "amount": 2500,
        "currency": "NGN",
        "convertedAmount": 2500,
        "amountExpected": 2500,
        "amountReceived": 2031.2,
        "varianceType": "underpayment",
        "message": "INCORRECT_AMOUNT",
        "actionRequired": "Process refund via dashboard/Refund API",
        "convertedCurrency": "NGN",
        "paymentMethods": [
            "bank_transfer"
        ],
        "defaultPaymentMethod": "bank_transfer",
        "redirectUrl": null,
        "customUrl": null,
        "successMessage": null,
        "settlementDestination": "wallet",
        "feeBearer": "customer",
        "reference": "fcr-p-05cf730d00",
        "merchantReference": "Test075",
        "isDisabled": false,
        "metadata": null,
        "status": "success",
        "createdAt": "2023-12-13T09:06:38.666Z",
        "updatedAt": "2023-12-13T09:09:11.000Z",
        "customer": {
            "name": "Test User",
            "email": "[email protected]",
            "phoneNumber": null
        }
    }
}
{
    "status": true,
    "message": "Charge verified",
    "data": {
        "id": 12098,
        "authorization": {
            "mode": null,
            "redirect": null,
            "metadata": null
        },
        "auth_model": null,
        "amount": 1500,
        "amountExpected": 1500,
        "amountReceived": 2031.2,
        "varianceType": "overpayment",
        "currency": "NGN",
        "fee": 30.57,
        "message": "INCORRECT_AMOUNT",
        "actionRequired": "Process refund via dashboard/Refund API",
        "status": "success",
        "reference": "dream032",
        "description": "",
        "type": "bank_transfer",
        "customer": {
            "name": "Joy Awili",
            "email": "[email protected]",
            "phoneNumber": null
        },
        "metadata": {},
        "settlementDestination": "wallet",
        "virtualAccount": {
            "bankName": "wema",
            "id": "6579711935064bef86c5144f",
            "bankCode": "035",
            "accountName": "FIN-The Masterpiece",
            "accountNumber": "7947689239",
            "sessionId": "239jw2387ssmnhkkkG2",
            "channelName": "wema",
            "payerAccountNumber": "7820444125",
            "payerAccountName": "Smartniggs Tino",
            "payerBankName": "gtb",
            "payerBankCode": "042",
            "expiresAt": "2023-12-13T09:13:46.000Z",
            "business": "60c1d7e39d9fc6b87dedb04e"
        }
    }
}

How to handle reversals for underpayments or overpayments

The refunds API allows the merchant to initiate refunds, which can be used to reverse overpayments and underpayments.

1 - Initiate a refund

To initiate a refund, you'll need to pass in the transaction reference for the transaction you would like to process a refund for. With this you can make a POST request to our initiate refund endpoint.

Endpoint:

{{host}}/collections/refund
curl --location 'https://api.fincra.com/collections/refund' \
--header 'accept: application/json' \
--header 'api-key: <Your API secret key>' \
--header 'Content-Type: application/json' \

Here's a sample payload to initiate a refund:

{
    "transReference": "5221daf0-6bb0-4c77-a38a-e7a71ea7aa69"
}

Response:

{
    "success": true,
    "message": "Collection refund initiated successfully",
    "data": {
        "reference": "e788b13b-3da4-43b8-9d9a-2f5e0c2dc3b0",
        "collectionMerchantReference": "",
        "collectionReference": "ce01c9ee-1633-43d6-88a1-0eb11824b4e5",
        "amountRefunded": 2,
        "collectionAmountExpected": 850,
        "collectionAmountReceived": 852,
        "collectionAmountSettled": 839.18,
        "collectionVarianceType": overpayment
    }
}

Note: When a refund has already been initiated on a transaction, you would get a 422 error with message "Refund already initiated"

2 - Get refund status

To get the status of a refund, you can query the get refund endpoint using the refund ID.

Endpoint:

{{host}}/collections/refund/{id}
curl --location 'https://api.fincra.com/collections/refund/{reference}' \
--header 'accept: application/json' \
--header 'api-key: <Your API secret key>' \
--header 'Content-Type: application/json' \

Response:

{
    "success": true,
    "message": "Refund fetched successfully",
    "data": {
        "id": 21,
        "collectionReference": "96d1fc1c-2570-4a50-8eca-1277c4a90c0d",
        "reference": "cba44033-bcaa-4df6-9e68-6b3cf7f251ca",
        "currency": "NGN",
        "amountRefunded": "5914.000000",
        "refundType": "overpayment",
        "payeeAccountNumber": "7821444125",
        "collectionExpectedAmount": "850.000000",
        "collectionAmountReceived": "6764.000000",
        "collectionAmountSettled": "6662.201800",
        "collectionSettlementReference": null,
        "collectionMerchantReference": "",
        "collectionVarianceType": "overpayment",
        "collectionVirtualAccountId": "655ec84f1f3ce30f00efad7f",
        "metadata": null,
        "refundStatus": "processing",
        "completedAt": "2023-12-07 17:39:00",
        "initiatedAt": "2023-12-05 15:07:18",
        "payee": {
            "email": null,
            "phone": null,
            "accountNumber": "7821444125",
            "bankCode": "042",
            "bankName": "gtb",
            "cardScheme": null
        }
    }
}