Credits

Introduction

A note on Trac in school settings

The primary use case for payment technologies in a schools setting is catering. While on the surface catering transactions seem relatively straightforward, they are made hugely more complex because of the rules and practice around Free School Meals (FSM).

The rules around Free School Meals require that:

  • Pupils who are entitled to FSM are able to purchase up to a defined amount each day
  • The unspent balance does not roll-over
  • They can spend more than this amount, if they have the cash, which in an epayments system means their free cash balance is positive
  • They can only spend this money on meals and not anything else

Ideally till systems would have sophisticated support for free school meals, providing catering staff with the information required to process transactions accurately. Tills do not typically provide this functionality. In particular they will typically only display a single balance for a pupil.

This means that in addition to the above we also require that:

  • Balances shown in payment systems, such as ParentPay, should not include any free school meal credit - i.e. they should show only the amount credited by parents, and debited by purchases that impact the free cash balance
  • Transactions show in payment systems should show all items purchased, but should only show the cost impact on the free cash balance, so that transaction totals correctly tally with the running balance
  • Balances shown on catering tills should include available free school meals credit
  • Balances shown on other tills should not include available free school meals credit
  • Any free school meals credit should be swept down after the end of any catering periods

Finally there are setup and reporting requirements:

  • Schools MIS systems typically provide for an FSM start date and end date on a per-pupil basis. On days that occur within these configured periods for each pupil they should be credited and debited appropriately around catering periods.
  • Schools want to report on the uptake of Free School Meals within their school, so wish to know how many pupils made use of their FSM provision

Our solution is comprehensive, and is able to accommodate the many and varied ways in which FSM is configured and administered. Integrators will need to understand these mechanisms to ensure they show balances required for their applications and debit the correct amount from the right places.

Purse types

There are 3 types of purse currently used in trac-core.

Default purse

title: Cash purseId: default type: cash

The default purse is created when a member is created. Members can only have one default purse.

In general the default purse represents the members ‘actual’ account balance. As sales are processed the default balance is updated through transfers. The default behaviour here is configured via organisation and installation defaults (see below).

Sales purse

title: Sales purseId: sales type: sales

The sales purse is created when a member is created. Members can only have one sales purse. The sales purse balance represents the unprocessed sales balance. Once a sale is processed the amount of the sale will be transferred to the default purse or a credit purse. Once all sales are processed the sales purse balance should be 0, i.e. it will always eventually converge to zero when transactions cease.

Credit purse

title: * purseId: * type: credit

Credit purses are created by posting to the purses endpoint /orgs/{org_id}/members/{member_id}/purses. The purseId is a ULID generated when the purse is created. The purse title is set in the request. purseId and purseTitle are both included in the meta data of a transaction. purseTitle can be used to identify a type of credit across members (for example in use on reporting on FSM and UIFSM).

The credit purse balance represents the value of the currently available credit for that member.

Credit purses can have additional fields that control how credit transactions are added and used.

  • validFrom: Timestamp from which this purse is valid. If this is not set then it is valid from the start of time.
  • validTo: Timestamp to which this purse is valid. If this is not set then it is valid until the end of time.
  • validSessions: List of sessions for which this purse is valid. If this is not set then it is valid for all sessions.

A credit purse has some additional required information in the credit namespace that controls what credit transactions are created and when:

  • amount: The amount of the credit transactions to be added.
  • creditApply: When a credit transaction should be added, in a crontab format.
  • expiryDuration: How many days a credit transaction is active for.

❗️Danger

Credit transactions should not be added more than once a day (i.e. the crontab should specify at most one occurrance daily)

🚧

Warning

If the credit related details of a purse change then the purse should be made inactive by setting the validTo date to the current date and a new credit purse created.

Transactions and purses

Sales

Transactions that are identified as ‘sales’ are sent to the sales purse. These are transactions that usually represent a purchase (in catering systems food and drink items or a ‘school meal’) and can include product information, often coming through a till system. They are transactions where a ‘credit’ can be used to pay for all or some of the transaction amount.

Sales are usually negative (indicating the members balance is reduced). A positive sale represents a refund.

❗️Danger

Refunds must only be processed for sales on the same day and in the same session as the refund is being created.

Behaviour if a refund is processed on subsequent days is undefined.

The transaction field state is always set to notProcessed for transactions. This means we can process transactions against credit before updating the members default purse balance.

Credit

Credit transactions are added automatically from a credit purse. The amount of the credit added and when it is added is controlled by values in the credit namespace. If these values are missing then no credit transactions will be added. Credits will only be applied if the current date is within the validTo and validFrom values on the purse. Credits added are always positive (increasing the balance).

When credit transactions are added they are set with an expiry in the credit namespace. The expiry is set to 00:00:00 at the start of the expiry day which is calculated from the transactionDate and expiryDuration from the credit purse. The creditCleared value in the credit namespace is set as NOT_CLEARED.

The transaction field state is always set to processed for transactions.

Clearing credit transactions

Credit transactions that have passed their expiry date and time are automatically cleared.

When a credit transaction is cleared the following things happen -

  1. If the creditUsageAmount on the credit is less than the credit amount a new creditCleared transaction with a negative amount is created to balance out the remaining credit.
  2. The credit transaction creditCleared field in the credit namespace is updated to be CLEARED.

Default

All other transactions are added to the default cash purse. These usually represent "top-up" amounts that increase the balance. Where systems allow money to be taken out of the account these can be negative refund amounts.

The transaction field state is always set to processed for transactions to the default purse.

Defaults

The defaults system provides a simple mechanism to configure on a per-integration, per-organization basis what the default values for anything within any entity that is created within the system by that integration. This provides a lot of flexibility where organisations have specific needs.

One of the key use cases for defaults is orchestrating which other integrations should synchronise transactions. By setting state fields within the appropriate namespaces, the defaults can ensure that transactions will be picked up by the required target integrations.

Purse level defaults

Furthermore, defaults can be configured in a purse basis, allowing detailed configuration of transactions depending on the purse that they target. This is done as follows within the defaults structure. In this example these are the defaults for the till integration. All till transactions should be synchronised, except sending them back to till or sending any refunds that impact credit to parentpay.

    {
        "transaction": {
            "default": {
                "till": {
                    "status": "unsynced"
                },
                "parentPay": {
                    "status": "unsynced"
                }
            },
            "sales":
                {
                    "till": {
                        "status": "synced"
                    },
                    "parentPay": {
                        "status": "unsynced"
                    }
                },
            "credit": {
                "till": {
                    "status": "unsynced"
                },
                "parentPay": {
                    "status": "synced"
                }
            }
        }
    }
📘

Selecting a purse for defaults

If a transaction purseId is anything other than sales or default the credit defaults will be used.

By using the defaults to set the status for different namespaces this allows transactions to be sent to or not sent to integrations based on the purse. Combined with integrations setting the related status on transactions originating in their system (cash transactions originating from a till system should not be sent back to the till) gives us sufficient control of what transactions are synced.

🚧

Ensuring no loopbacks

Transactions should always set their own status in their namespace to synced to ensure they do not loop back and fetch their own transactions.

Sales processing

Sales

Sales have a negative value.

Sales processing flow

Refunds

Refunds have a positive value.

Refund processing flow