Theme

Start for free
DocsLemon Squeezy

03 · Revenue

Connect Lemon Squeezy and link its orders to visits

Tally reads your store's orders and subscription invoices, and adds its own webhook.

01Connect Lemon Squeezy

  1. Create an API key in Lemon Squeezy

    Lemon Squeezy's API keys can't be limited, so make one just for Tally: Tally only reads your store's orders and adds its own webhook, and you can delete the key at any time. In Lemon Squeezy, open Settings, API and create a key. This is all Tally does with it:

    PermissionAccessWhy
    OrdersReadEvery sale: its amount, tax and refunds, and the customer's email (hashed at once, never stored).
    Subscription invoicesReadRenewals and plan changes.
    WebhooksWriteTally adds its own webhook to the store, so payments arrive within seconds.

    A key made in test mode sees only test orders: use one to try it first.

  2. Paste the API key

    On your site's Integrations page (or the Payments step when you add a site), open Lemon Squeezy and fill in:

    • API key: Tally stores it encrypted and never shows it again.
    • Store (optional): Only if your key sees more than one store: its address, name or id.

    Then press Connect Lemon Squeezy.

  3. Tally checks it and imports the past year

    It checks the key against your store, adds its own webhook so new orders arrive as they happen, and imports the past 365 days. Lemon Squeezy sends a checkout's custom data only in webhooks, so imported orders are linked by the customer's email; orders from now on carry the visitor id.

02Pass the visitor's id as custom data

On pages with buy links, a few lines add the _tally_vid cookie's value as checkout[custom][tally_visitor_id] to every Lemon Squeezy checkout link. A checkout your server creates takes it in checkout_data.custom.

Show the code for

// On pages with Lemon Squeezy buy links: add the visitor id as custom data.
const visitorId = (document.cookie.match(/(?:^|; )_tally_vid=([\w-]+)/) || [])[1];
for (const link of document.querySelectorAll('a[href*=".lemonsqueezy.com/checkout/buy/"]')) {
  const url = new URL(link.href);
  if (visitorId && !url.searchParams.has('checkout[custom][tally_visitor_id]')) {
    url.searchParams.set('checkout[custom][tally_visitor_id]', visitorId);
    link.href = url.href;
  }
}

// Express with cookie-parser, where you create a Lemon Squeezy checkout.
app.post('/checkout', async (req, res) => {
  const visitorId = req.cookies['_tally_vid'];
  const response = await fetch('https://api.lemonsqueezy.com/v1/checkouts', {
    method: 'POST',
    headers: {
      Accept: 'application/vnd.api+json',
      'Content-Type': 'application/vnd.api+json',
      Authorization: 'Bearer ' + process.env.LEMONSQUEEZY_API_KEY,
    },
    body: JSON.stringify({
      data: {
        type: 'checkouts',
        attributes: { checkout_data: { custom: visitorId ? { 'tally_visitor_id': visitorId } : {} } },
        relationships: {
          store: { data: { type: 'stores', id: '1' } },
          variant: { data: { type: 'variants', id: '1' } },
        },
      },
    }),
  });
  const { data } = await response.json();
  res.redirect(303, data.attributes.url);
});

03On a cookieless site

With no cookie there's no id to add, so identify the buyer with the email they pay with: goals and identify.

04Check it worked

05What isn't read

Something missing, or not what you see? Write to support@tallybyoutpost.com and say which page.