Till System Integrators Guide

Authentication

See Getting Started

Members and member IDs

Before posting a transaction for a member you will need to acquire the relevant memberId. You can locate a member using another shared identifier you already have access to, such as the UPN or MIS ID.

You locate the relevant memberID by adding query parameters for the index you wish to search. For example to query by misId you can do the following:

    GET https: //trac-demo.stage.pebble.cloud/api/v1/orgs/{org_id}/members?core.memberType.eq=pupil&mis.misId.eq=123
    [
        {
            ...
        }
    ] // list of members

See Filters for more help on how to search and filter.

Sales transactions without members

All transactions should be against a purse and a member. You may have sales that do not have an identified member. This can occur for example when:

  • Sales are made for actual cash to anonymous individuals
  • Sales are made against an account to unidentified individuals in a group account, for example contractors or visitors

Where these types of sale are anticipated, member accounts should be created for these accounts in the organization. The till integration should create these as required. These accounts should not have identifiers from the MIS system, because they will not synchronise with it.

To create an account of this type, use this endpoint:

    POST https: //trac-demo.stage.pebble.cloud/api/v1/orgs/{org_id}/members

With this request body:

    {
        "isActive": true,
        "externalIds": {
            "<integration>-<idtype>": "",
        },
        "displayName": "Contractors",
        "memberType": "system"
    }

And you will get a response of the form:

    {
        "memberId": "..."
    }
fielddescription
isActiveShould always be true
externalIdsYou should select an appropriate key name for your id, which is prefixed with the name of your integration, to avoid collision.
displayNameThis is used in reporting
memberTypeThis will be one of pupil, staff, visitor or system. use system for accounts which do not represent known individuals.

You should use the externalId field to hold your own identifier for these groups. You should keep track of the memberId returned, and use this for future sales transactions against these accounts. Note that these accounts will show a negative balance in Trac over time, because they will not receive top-ups.

Roaming members

You may have cases where members in one organization are entitled to make purchases in other organizations that you also manage.

You should create a "roaming" member account, as above, for the organization in which purchases are made. You should then create two transactions for each sale:

  1. A transaction in the member's organization, against the member, for a sale with the total amount (and sourceOfFunds etc as required) but without the product details, and
  2. A transaction in the organization in which the purchase is made, against the new Roaming account, that has the product details and amount

Purses

See Understanding Purse Management.

Posting sales transactions

Sales are posted to the sales purse using the transactionscreate endpoint, documented here. The sales purse has the static identifier sales.

See purse descriptions and Trac sales processing logic for more information.

Minimum mandatory sale

Sale amounts are negative, because they deduct from the member's balance. Currency values are sent as strings in pounds, with two decimal places of pence.

All dates are in ISO8601 format in UTC - i.e. ending in Z.

    POST https: //trac-demo.stage.pebble.cloud/api/v1/orgs/{org_id}/members/{member_id}/purses/sales/transactions
    {
        "amount": "-10.00",
        "transactionDate": "2004-10-11T12:24:12Z",
        "type": "sale",
        "vatCode": "S",
        "till": {
            "vatAmount": "2.00",
            "vatAmountAsInteger": 200,
            "salePayments": {
                "ACCOUNT": {
                    "paymentTotal": "10.00"
                }
            },
            "paymentMethods": [
                "ACCOUNT"
            ]
        }
    }
fielddescriptionStatus
amountA string representation of the value. It should be negative for a sale and in pounds and pence.mandatory
transactionDateThe date and time the transaction took place, in ISO8601 format, in UTC.mandatory
typeAlways sale for a sale.mandatory
vatCodeUse S for standard unless the entire sale is one of the other categories. If the entire sale has a VAT amount that is not standard, use the most relevant code, see VAT code table below. Note that school meals should be X unless otherwise stated.mandatory
till.vatAmountA positive value, the amount of the sale which is VAT.mandatory
till.vatAmountAsIntegerThe same value in integer pence.mandatory
till.salePaymentsThis represents the physical sources of the funds used to pay for the transaction. The possible keys you can include are listed below. You should include a paymentTotal for any that are used. NB: If CASH, CARD or CHEQUE are used, you should also send a top-up to the default purse for the amount.mandatory
till.paymentMethodsShould contain a list of the methods used, which are the keys in salePayments.optional
till.productSalesThis represents each line item of a sale. You should include the productName, sellingPrice (The price of the product sold inclusive of VAT), productVatRate (which should be a string with a percent sign, e.g. "2%"), plu (which can be a blank string, but the field itself cannot be ommitted), quantity and, if the line item was discounted, discountAmount.mandatory

VAT Codes

VAT CodeMeaning
SStandard (currently 20%)
RReduced (currently 5%)
CCustom
OOutside
ZZero rated
XExempt of VAT (pupil day meals are exempt of VAT as are staff duty meals)

In line with HMRC advice, VAT should be rounded up to the nearest penny when rounding is required.

Sale payment sources

Payment Source
ACCOUNT
CASH
CARD
CHEQUE

Credit as payment for sales

Where any part of a sale has been paid for using credits, you must provide details on the quantity and types of credit used, so that reporting is correct. This can be done on a per-sale or per-item basis.

    {
        "amount": "-10.00",
        "transactionDate": "2004-10-11T12:24:12Z",
        "type": "sale",
        "sourceOfFunds": {
            "fsm": {
                "amount": "2.50"
            }
        },
        "till": {
            "salePayments": {
                "ACCOUNT": {
                    "paymentTotal": "10.00"
                }
            },
            "paymentMethods": [
                "ACCOUNT"
            ]
        }
    }

This transaction is a total of £10.00, of which £2.50 came from a free school meal account and the rest is covered by the members' default purse.

Including products in a sale

If you have product data, you should provide it. See Products

Other recommended fields

tags

You can add any tags you wish to transactions, in the tags object. You should be careful in selecting keys to ensure you do not collide. We recommend you prefix tag keys with the name of your integration or product.

externalIds

Contains references to any identifiers you have for the sale. As with tags you can add any identifiers you wish. Prefix your keys with your integration or product name.

description

You can use this to annotate the sale with any other information you have, such as the contents of the sale, if you do not have formal product information.

Refunds

A refund transaction indicates that some or all of a previous sale has been refunded to the customer. These should only be provided on the same day as the sale. If an amount is refunded after that same day it should be sent as a top-up, not as a refund. This is because credit sweepdowns and aggregate daily reporting will have been performed.

This document explains how Trac processes refunds.

To refund the following sale within the same day:

    {
        "amount": "-10.00",
        "transactionDate": "2004-10-11T12:24:12Z",
        "type": "sale",
        "vatCode": "S",
        "till": {
            "vatAmount": "2.00",
            "vatAmountAsInteger": 200,
            "salePayments": {
                "ACCOUNT": {
                    "paymentTotal": "10.00"
                }
            },
            "paymentMethods": [
                "ACCOUNT"
            ]
        }
    }

You should send:

    {
        "amount": "10.00",
        "transactionDate": "2004-10-11T12:25:00Z",
        "type": "sale",
        "vatCode": "S",
        "till": {
            "vatAmount": "-2.00",
            "vatAmountAsInteger": -200,
            "salePayments": {
                "ACCOUNT": {
                    "paymentTotal": "-10.00"
                }
            },
            "paymentMethods": [
                "ACCOUNT"
            ]
        }
    }

As you can see this is a reverse of the sale for all numeric values.

If only a partial amount is refunded, the amount, vatAmount and paymentTotal should represent the amount refunded.

Where some or all of a transaction is paid with physical cash, this might look like the following:

    {
        "amount": "-10.00",
        "transactionDate": "2004-10-11T12:24:12Z",
        "type": "sale",
        "vatCode": "S",
        "till": {
            "vatAmount": "2.00",
            "vatAmountAsInteger": 200,
            "salePayments": {
                "CASH": {
                    "paymentTotal": "10.00"
                }
            },
            "paymentMethods": [
                "CASH"
            ]
        }
    }

Note that any sales made where payment is proffered by the customer at the point of sale should include a top-up to their default purse for the value of the amount provided, to ensure balances are correct.

So in this case, the sale would have a matching top-up to cover the cash payment of £10.00:

    {
        "amount": "10.00",
        "transactionDate": "2004-10-11T12:24:12Z",
        "type": "cash"
    }

Therefore the refund would look like the following:

    {
        "amount": "10.00",
        "transactionDate": "2004-10-11T12:25:00Z",
        "type": "sale",
        "vatCode": "S",
        "till": {
            "vatAmount": "-2.00",
            "vatAmountAsInteger": -200,
            "salePayments": {
                "CASH": {
                    "paymentTotal": "-10.00"
                }
            },
            "paymentMethods": [
                "CASH"
            ]
        }
    }

You should then debit the default account, because you have provided cash to the customer at the point of sale:

    {
        "amount": "-10.00",
        "transactionDate": "2004-10-11T12:24:12Z",
        "type": "cash"
    }

Refunds where part or all of a sale is provided on credit

If the refund occurs on the same day, refunds should go back to the purses that were used to provide payment.

See the documentation on credit management

To refund this sale on the same day:

    {
        "amount": "-10.00",
        "transactionDate": "2004-10-11T12:24:12Z",
        "type": "sale",
        "sourceOfFunds": {
            "fsm": {
                "amount": "2.50"
            }
        },
        "till": {
            "salePayments": {
                "ACCOUNT": {
                    "paymentTotal": "10.00"
                },
            }
            "paymentMethods": [
                "ACCOUNT"
            ]
        }
    }

You should send:

    {
        "amount": "10.00",
        "transactionDate": "2004-10-11T12:24:12Z",
        "type": "sale",
        "sourceOfFunds": {
            "fsm": {
                "amount": "-2.50"
            }
        },
        "till": {
            "salePayments": {
                "ACCOUNT": {
                    "paymentTotal": "-10.00"
                }
            },
            "paymentMethods": [
                "ACCOUNT"
            ]
        }
    }

This will result in a zero end balance after the reversion. Note that our sales processing where the sale is on the same day as the refund will revert debits in the correct order, ensuring balances are correct.

Subsequent days

You should send top-ups for cash accounts as above. You should not send a top-up for credit accounts on subsequent days.

Cancellations

You should not send cancellations. You should just not send any cancelled sale. If you have sent a sale which is subsequently cancelled you should process it as a refund, because the sale may have been synchronised with onward systems.

Posting top-ups

If you are integrated with an online payment provider, you should send all top-ups you receive.

These should be posted to the default purse.

An example top-up is shown below:

    POST https: //trac-demo.stage.pebble.cloud/api/v1/orgs/{org_id}/members/{member_id}/purses/default/transactions
    {
        "amount": "10.00",
        "transactionDate": "2004-10-11T12:24:12Z",
        "type": "epayment"
    }

All account top-ups should have type of epayment. Note: Top-ups made through Re-val machines should instead use a type of cash.

Integrating products

Kitchen Management Systems

Where there is a kitchen management system that has unique identifiers for products we expect the product code or identifier to be in the field called plu.

Trac will also potentially be integrated with the kitchen management system (KMS), and we will provide inventory management features for the KMS, based on the product data you send with sales transactions.

Any plu you send which is not recognised by the KMS will not be sent to the KMS and therefore the inventory will be incorrect, so it is important to ensure that plus match successfully. Sales financial data will still be processed if plus are not matched, but the sale will not be sent to the KMS.

Products

Products should be listed by you in the till.productSales field.

The minimum you can provide for each product is as follows:

    {
        "till": {
            "productSales": [
                {
                    "plu": "42",
                    "sellingPrice": "1.50",
                    "productVatRate": "20%",
                    "quantity": 1,
                    "productName": "packet of crisps"
                }
            ]
        }
    }

Promotions and discounts

Individually discounted items

Discounted products should be posted with an actual discount value on a product line. The sellingPrice in this case is the actual (discounted) amount paid, and the vat amount is the actual vat paid.

    {
        "amount": "-2.00",
        "transactionDate": "2004-10-11T12:24:12Z",
        "type": "sale",
        "vatCode": "S",
        "till": {
            "vatAmount": "0.40",
            "vatAmountAsInteger": 40,
            "salePayments": {
                "ACCOUNT": {
                    "paymentTotal": "2.00"
                }
            },
            "paymentMethods": [
                "ACCOUNT"
            ],
            "productSales": [
                {
                    "plu": "42",
                    "sellingPrice": "1.50",
                    "productVatRate": "20%",
                    "discountAmount": "0.20",
                    "quantity": 1,
                    "productName": "packet of crisps"
                },
                {
                    "plu": "60",
                    "sellingPrice": "0.50",
                    "productVatRate": "20%",
                    "discountAmount": "0.00",
                    "quantity": 1,
                    "productName": "freddo"
                }
            ]
        }
    }

In this example, the list price for the packet of crisps is £1.70.

Group discounts

A group discount is a discount where a group of items triggers a discount that applies to the sale as a whole, for example a "meal deal".

You should redistribute group discounts across the individual discounted items, and send them as above.

Redeemed items

In cases where an item such as a glass bottle is redeemed to pay for part of a sale, this should be processed as a cash top-up before the sale is made, just as with a standard sale where part of it is covered by cash.