Debt Management

Feature used to ensure balances are positive

A debt is created as soon as the authorization balance goes negative. As a consequence, debts can only be created on offline transaction and P2P.

❗️

It is important to note that the debt carries two concepts:

  • The creation of an internal transfert completed to cover the balance shortfall
  • The notion of debt owed by the end user to the partner, with its own status.

Attibute definition

The DebtCreatedOrUpdated callback is received in 2 cases:

  1. a debt was created
  2. an existing debt changed

The remainingAmount or/and the recoveryStatus changed.

The internalDebtTransfer is dedicated to the internal transfer created from the P&L account to the enduser's account to cover the balance shortfall.


Debt status

stateDiagram

[*] --> InProgress
InProgress --> ClosedWithRecovery
InProgress --> ClosedWithoutRecovery
ClosedWithRecovery --> [*]
ClosedWithoutRecovery --> [*]


Debt operation in the webdesk

A line labeled "Debt" is created when the debt is initiated. This line simultaneously represents the InternalDebtTransfer and the current status of the debt.

The operation status is "Completed" even if the debt status (visible in the Webdesk) initially shows as "In Progress."


Debt creation use cases

Internal transfer

When the processUnpaid attribute is set to true, if the amount of the internal transfer exceeds the account balance, a debt is created.

Example:

sequenceDiagram
autoNumber

Participant Partner
Participant XPO

Note over Partner, XPO: Accounting balance 20€ <br/> Authorisation balance 20€ <br/> Total debt: 0€


Partner ->> XPO: POST /api/v2.0/internal-transfers <br/> {amount: 30€, processUnpaid = true}
XPO ->> Partner: HTTP/200
XPO -->> Partner: Callback InternalTransfer

Note over Partner, XPO: The authorization balance can NOT be negative.<br/> A debt is created,  with <br/> debt amount = inital P2P amount - authorisation balance

XPO ->> XPO: Internal debt transfer <br/> {accountId:partner's profit&loss account, <br/>recipient: user, <br/>amount: 10€}
XPO -->> Partner: Callback InternalDebtTransferCreated


XPO ->> XPO: Debt Creation
XPO -->> Partner: Callback DebtCreatedOrUpdated

Note over Partner, XPO: Accounting balance 0€ <br/> Authorisation balance 0€ <br/> Total debt: 10€






Offline card operations

sequenceDiagram
autoNumber

Participant Merchant
Actor User
Participant Partner
Participant XPO

Note over Merchant, XPO: Accounting balance 20€ <br/> Authorisation balance 20€ <br/> Total debt: 0€

User ->> Merchant : Offline Transaction 30€

break wainting for the clearing
    Merchant-->>XPO: Clearing
end
XPO -->> Partner: Callback CardOperationCreatedOrUpdated

Note over Partner, XPO: The authorization balance can NOT be negative. <br/> A debt is created,  with <br/> debt amount = clearing amount - authorisation balance
XPO ->> XPO: Internal debt transfer <br/> {accountId:partner's profit&loss account, <br/>recipient: user, <br/>amount: 10€}
XPO -->> Partner: Callback InternalDebtTransferCreated
XPO ->> XPO: Debt Creation
XPO -->> Partner: Callback DebtCreatedOrUpdated

Note over Partner, XPO: Accounting balance 0€ <br/> Authorisation balance 0€ <br/> Total debt: 10€

Note about offline operation (refer to the dedicated topic for more details)

Card X0X

X0X cards have almost systematic authorisation. Exceptions are made in certain cases:

  • parking
  • toll
  • aircraft

In these cases, if the Payment Temrinal does not request authorisation, the transactions will still be accepted.

Card X2X

X2X cards are systematic authorisation cards. This information is written directly onto the chip. It will therefore be impossible to make payments on tpe that do not require authorisation.

Forced operation on the TPE

❗️

Despite this feature of the chip, there are POS terminals that allow you to force the operation offline.

In this case, no preventive action is possible on the Xpollens side: the transaction is carried out, the compensation is received and deducted from the enduser's account.


R-transactions

Recall SDD OUT during the first 8 weeks afer the payment date

If an SDD credited to the Xpollens account is recalled by the debtor bank within 8 weeks, Xpollens is obliged to return the funds regardless of the account balance. This can therefore create a debt.


Debt recovery

❗️

The management of debt collection is entirely up to you.

You can debit the account, adjust the remaining balance, or update the debt status as needed.

Xpollens does not take any action.

It is important to note that debts must be closed transaction by transaction (debt by debt).

Debt recovery: as soon as the account balance is positive, recovered with fund recovery

Here is a proposed debt recovering sequence:

  1. As soon as funds are deposited into the account
  2. Review the outstanding debts
  3. Recover as much as possible while there are funds available
sequenceDiagram
autoNumber

Actor User
Participant Partner
Participant XPO

Note over User, XPO: Accounting balance 0€ <br/> Authorisation balance 0€ <br/> Total debt: X€

User ->> XPO : bank account loading (topup, sct in, ip in, ...)
XPO -->> Partner: Callback associated to the transaction type

Partner ->> Partner: existing open debts ?<br/> (internal request in your backend)

alt Level 1: If existing open debt(s)
    Partner ->> XPO : GET /api/v3.0/debts {userId}
    XPO -->> Partner: list debt amounts {remainingAmount, recoveryStatus}
    
    alt Level 2: as long as sum open debt amounts != 0€ & user balance > 0€
                    
                    loop Level 3: Recover the funds & close the debt.
                        Partner ->> XPO: POST /api/v2.0/internal-transfers <br/>{accountId: userId, recipient: partner's P&L account, amount = Y} 
                        XPO -->> Partner: Callback InternalTransfer
                        Partner ->> XPO: PATCH /api/v3.0/debts/{debtId} <br/>  {remainingAmount:0,  status: ClosedWithRecovery}
                        XPO -->> Partner: Callback DebtCreatedOrUpdated

                    end

        else Level 2: when sum open debt amounts = 0€ or user balance = 0€, stop the loop

else Level 1: No open debt
end

end
 



The debt must be recorded in the partner's accounts.


Transaction IN to monitor to recover debt

  • Top-up card
  • SCT IN & IP IN
  • SCT OUT cancellation
  • Refund SCT OUT & IP OUT
  • SDD OUT
  • SDD IN refund
  • SDD OUT cancellation
  • Card operation IN
  • Card authorisation expiration

Debt recovery: recovey without fund recovery

You can decide to close the debt without recovering the funds. The losses will then be your responsibility.

sequenceDiagram
autoNumber

Actor User
Participant Partner
Participant XPO

Note over User, XPO: Accounting balance 0€ <br/> Authorisation balance 0€ <br/> Total debt: X€

    Partner ->> XPO : GET /api/v3.0/debts/{debtId}
    XPO -->> Partner: debt {remainingAmount, recoveryStatus}
    
                        Partner ->> XPO: PATCH /api/v3.0/debts/{debtId} <br/>  {remainingAmount:X,  status: ClosedWithoutRecovery}
                        XPO -->> Partner: Callback DebtCreatedOrUpdated

Note over User, XPO: loop if you want to close more than 1 debt without recovery



Technical information

Internal Debt transfer

The payload is essentially the same as for internal transfer.

🔗Debt transfer

About the callback : 🔗InternalDebtTransferCreated

Debt


Best pratice: how to display debt in your app

Display a debt balance in addition to the account balance

sequenceDiagram
autoNumber

Actor User
Participant Partner
Participant XPO

Note over User, XPO: Accounting balance 0€ <br/> Authorisation balance 0€ <br/> debt Balance: X€

User ->> Partner : display account balance

Partner ->> XPO : GET /api/v2.0/accounts/{accountId}
XPO ->> Partner : {availableBalance= 0€}

Partner ->> Partner: existing debt? Yes
Partner ->> XPO : GET /api/v3.0/accounts/{accountId}/debt-balance
XPO -->> Partner: value (X)

Partner ->> User: display 2 balances separately: account balance & debt balance



Debt displayed on account statements

The amount of the debt is not shown on the account statement.

The InternalDebtTransfer as the InternalTransfer are marked "Paiement" in the bank statement.


How to test

Create a debt with a P2P - example Account management fees

1- GET /api/v3.0/accounts/accountId

{
    [...]
    "authorizationBalance": "authorizationBalance",
    [...]
}

2- Create a P2P that respects the limits, with an amount greater than the balance

POST /api/v2.0/internal-transfers

"Payload": {
  "internalTransferId": "ExternalP2PRef",
  "accountId": {{accountId}},
  "amount": {
    "value": authorizationBalance+1,
    "currency": "EUR"
  },
  "recipient": {
    "accountId": "Profit_and_lost_accountId"
  },
  "extraDatas": {
    "description": "Custom description",
    "label1": "Label",
    "label2": "SubLabel",
    "label3": "Tag"
  },
  "processUnpaid": true,
    
}

3- You receive the callback

{
  "type": "DebtCreatedOrUpdated",
  "data": {
    "debtId": debtId,
    "originTransactionId": "ExternalP2PRef",
    "accountId": {{accountId}},
    "accountHolderId": {appuserId},
    "creationDate": "YYYY-MM-DDTHH:MM:SS.000Z",
    "amount": {
      "value": "1.00",
      "currency": "EUR"
    },
    "remainingAmount": {
      "value": "1.00",
      "currency": "EUR"
    },
    "recoveryStatus": "InProgress"
  }
}

Create a debt with an offline operation

1- GET /api/v3.0/accounts/accountId

{
    [...]
    "authorizationBalance": "authorizationBalance",
    [...]
}

2- Create an offline operation that respects the limits, with an amount greater than the balance POST /v2.0/card-operations/simulate-offline-settlement

{
  "cardId": {{cardId}},
  "amount": {
    "value": authorizationBalance+1,
    "currency": "EUR"
  },
  "operationDate": "2024-07-02T07:23:36.915Z",
  "direction": "Debit", // Debit Credit
  "merchantName": "Discraft Disc",
  "merchantCategoryCode": "5941"
}

3- You receive the callback

{
  "type": "DebtCreatedOrUpdated",
  "data": {
    "debtId": debtId,
    "originTransactionId": "x",
    "accountId": {{accountId}},
    "accountHolderId": {{appuserId},
    "creationDate": "YYYY-MM-DDTHH:MM:SS.000Z",
    "amount": {
      "value": "1.00",
      "currency": "EUR"
    },
    "remainingAmount": {
      "value": "1.00",
      "currency": "EUR"
    },
    "recoveryStatus": "InProgress"
  }
}

FAQ


Did this page help you?