blog

Photo of receipt by Michael Walter on Unsplash

Sales Receipt File Format (part 2)

by | Jul 19, 2013 | Developer Blog | 3 comments

Last time, I rambled for a while about what it might be like to have a file format for itemizing a sales receipt. This spurred a bit of conversation, which was helpful. In particular, Troy Farrell offered some very sound observations, including that printed barcodes could make for a great transit mechanism, although the amount of data I’m interested in cannot be simply packed into a barcode, at least not a QR code. Troy’s suggestion for the barcode to represent a URL leading to the rest of the data is a smart compromise. And although I do miss manipulating the byte-wise data structures of the DOS era, one of the directives here is for the format to be human-editable Unicode text.

I have been very tempted to use YAML because of its seductive lack of extraneous punctuation. However, the full YAML specification is many times as complicated as JSON. And it’s not quite as readily supported as JSON. And the reliance on significant whitespace is perhaps a liability for the not necessarily computer savvy target audience. So I must regrettably concede that JSON presents a smaller "Murphy’s Law" factor.

I also toyed with a recursive structure, but for similar reasons, I quickly nixed it. It could be prudent to impose a self-checking hierarchy requiring all of the embedded totals to balance out. But that would make representing basic cases needlessly complex, at least from the simple expectations of a paper receipt. There’s a reason nobody uses the MNG file format. It’s for this reason that most of the fields are optional, and that subtotals are expected only to be informative, not to be validated. Still, I think this is worth debating.

It would be premature to write out a long, boring specification at this point. But it’s never too soon to whip out a mockup and see if people have something to say about it. You’ll just have to question anything that isn’t self-evident.

[
    {
        "time": "2010-05-07T15:15-05:00",
        "seller": "ARCO",
        "number": 96060,
        "currency": "USD",
        "items":
        [
            {
                "quantity": 8.549,
                "units": "gallons",
                "item": "Unleaded",
                "details": "Pump 03",
                "each": 2.999,
                "price": 25.64,
            }
        ],
        "total": 25.64,
        "payments":
        [
            {
                "method": "MC Debit",
                "payment": 25.64,
                "account": "############4321"
            }
        ]
    },
    {
        "time": "2011-06-20T03:41:14-07:00",
        "seller": "Walmart",
        "sellerDetails": "Save money. Live better.n( 520 ) 568 - 0846nMANAGER KEVIN D.n41650 W MARICOPA CASA GRANDE HWY",
        "number": 12345,
        "currency": "USD",
        "items":
        [
            {
                "item": "BGN CLNS25OZ",
                "gtin": 003810014852,
                "price": 8.98,
                "excluded": [{ "label": "TAX 1", "rate": 0.087, "amount": 0.78 }]
            },
            {
                "item": "BAL DK  PNT",
                "gtin": 075004918040,
                "price": 0.98,
                "excluded": [{ "label": "TAX 2", "rate": 0.02, "amount": 0.02 }]
            },
            {
                "item": "MILO KCN MTB",
                "gtin": 007910050888,
                "price": 2.62,
                "excluded": [{ "label": "TAX 1", "rate": 0.087, "amount": 0.23 }]
            },
            {
                "item": "DOG FOOD",
                "gtin": 001780013476,
                "price": 13.86,
                "excluded": [{ "label": "TAX 1", "rate": 0.087, "amount": 1.21 }]
            }
        ],
        "subtotals":
        [
            { "label": "SUBTOTAL", "amount": 26.44 },
            { "label": "TAX 1", "rate": 0.087, "amount": 2.22 },
            { "label": "TAX 2", "rate": 0.02, "amount": 0.02 },
        ],
        "total": 28.68,
        "payments":
        [
            {
                "payment": 28.68,
                "method": "AmEx Charge",
                "account": "**** ****** *4321",
                "approval": 123456,
            }
        ],
        "change": 0.00,
        "message": "Ask a Pharmacy Sales Associate how youn  can save money on bird medications!"
    },
    {
        "time": "2007-07-30T13:29:17+01:00",
        "seller": "Berghotel Grosse Scheidegg",
        "number": 4572,
        "currency": "CHF",
        "items":
        [
            {
                "quantity": 2,
                "item": "Latte Macchiato",
                "each": 4.50,
                "price": 9.00,
                "included": [{ "label": "MwSt", "rate": 0.076, "amount": 0.64 }]
            }
            {
                "quantity": 1,
                "item": "Gloki",
                "each": 5.00,
                "price": 5.00,
                "included": [{ "label": "MwSt", "rate": 0.076, "amount": 0.35 }]
            },
            {
                "quantity": 1,
                "item": "Schweinschnitzel",
                "each": 22.00,
                "price": 22.00,
                "included": [{ "label": "MwSt", "rate": 0.076, "amount": 1.55 }]
            },
            {
                "quantity": 1,
                "item": "Chaesspaetzli",
                "each": 18.50,
                "price": 18.50,
                "included": [{ "label": "MwSt", "rate": 0.076, "amount": 1.31 }]
            },
        ],
        "subtotals": [{ "label": "MwSt", "rate": 0.076, "amount": 3.85 }],
        "total": 54.50,
        "payments":
        [
            {
                "method": "Bargeld",
                "payment": 60.00
            }
        ],
        "gratuity": 5.50,
        "message": "Entspricht in Euro: 36.33 EURnEs bediente Sie: Hilde"
    },
    {
        "time": "2011-02-28T13:41:41Z",
        "seller": "Pret A Manger",
        "number": 01903000293,
        "currency": "GBP",
        "items":
        [
            {
                "quantity": 1,
                "item": "BLOOMER - CHICKEN & PESTO",
                "price": 2.99,
                "included": [{ "label": "VAT", "rate": 0.0000, "amount": 0.00 }]
            },
            {
                "quantity": 1,
                "item": "PURE PRET STIL - POMEGRAN",
                "price": 1.50,
                "included": [{ "label": "VAT", "rate": 0.2000, "amount": 0.25 }]
            },
        ],
        "total": 4.49,
        "payments":
        [
            { "method": "AMEX CHARGE", "payment": 4.49 }
        ]
    },
    {
        "time": "2013-01-16T19:22-07:00",
        "seller": "Amazon.com LLC",
        "number": "112-9056007-4496257",
        "currency": "USD",
        "items":
        [
            {
                "item": "Jacques Pepin: Cooking with Wine",
                "details": "Condition: New",
                "gtin": 1234567890123,
                "price": 21.95,
                "excluded":
                [
                    { "label": "Shipping & Handling", "amount": 2.99 },
                    { "label": "Sales Tax", "rate": 0.000, "amount": 0.00 }
                ]
            },
            {
                "item": "Something About Dragons",
                "details": "By: Jane YolennCondition: Used",
                "gtin": 9780152051266,
                "price": 3.99,
                "excluded":
                [
                    { "label": "Shipping & Handling", "amount": 1.99 },
                    { "label": "Sales Tax", "rate": 0.085, "amount": 0.34 }
                ]
            }
        ],
        "subtotals":
        [
            { "label": "Items Subtotal", "amount": 25.94 },
            { "label": "Shipping & Handling", "amount": 4.98 },
            { "label": "Super Saver Discount", "amount": -4.98 },
            { "label": "Total before tax", "amount": 25.94 },
            { "label": "Sales Tax", "amount": 0.34 }
        ],
        "total": 26.28,
        "buyer": "Bradley Macomber",
        "buyerDetails": "PO Box 10426netc.",
        "payments":
        [
            {
                "method": "Gift Card",
                "payment": 20.00
                "account": "Last digits: 8765",
            },
            {
                "method": "AmEx Charge",
                "payment": 6.28,
                "account": "Last digits: 4321",
                "approval": "A2345"
            }
        ]
    }
]

This file describes five transactions. Maybe I went overboard collecting examples, but I need to cover a variety of scenarios.

Mandatory fields are: time, seller, currency, items, item, price, total, payments, payment, method. The field names are fixed, which is to say made-up fields should be avoided.

Time should be given in W3C format, which is a subset of ISO format.

The *details and message fields are meant to allow arbitrary, multi-line text, whereas most fields are meant to be one-liners.

The currency is mandatory and should be a 3-letter ISO currency code. Thus, all currency values should be given as pure numbers without any labeling.

The account field of a payment is convenient but must not be numeric. Apparently some people still don’t know that you can’t print out people’s full credit card numbers on receipts. Well, you can, but a class-action lawsuit will force you to recompense every single visitor to your establishment during the period in which you printed such receipts, even for those who weren’t actually slighted. It wouldn’t be good for business.

I’m aiming for a lot of flexibility, but there are pros and cons to this approach. It would be nice to make more of the fields mandatory and standardize the values, but this would require a more thorough understanding of common cases. There are a lot of different taxes and fees in the world. Hopefully people will be inclined to point them out here.

Perhaps the subtotals array could be mandatory, or perhaps the labels of the subtotals could be expected to match the labels of the excluded amounts per item. It’s a little demanding to make that mandatory, but it would set a useful precedent. The idea is that it’s nice for the merchant to break down fees associated with each item, but it would be even better if the subtotals would always add up as a self-check. The problem is that there are so many different ways to subtotal that it’s hard to say there should be a "tax" field and a "taxType" field and a "shipping" field and expect to cover all cases as easily.

Anyway, please shred up this receipt format. What is overlooked? How might a completely different approach be better? What’s a good way to make it possible to self-check subtotals of all these different forms of taxes and fees while keeping the format about as straightforward as it appears in my example?


Do people actually pay for this service? Isn’t this business setting itself up for fraud charges? Seems like the sort of thing you’d want to keep underground, by which I mean in your college dorm room.

+ more

Accurate Timing

Accurate Timing

In many tasks we need to do something at given intervals of time. The most obvious ways may not give you the best results. Time? Meh. The most basic tasks that don't have what you might call CPU-scale time requirements can be handled with the usual language and...

read more