An Elixir client for the API and Webhooks of LemonSqueezy.
def deps do
[
{:lemon_ex, "~> 0.2.3"}
]
end
To make API calls, you need to create an API key first. If you also want to handle webhook requests, you need to create a webhook secret too. See the section below for tips on how to generate the webhook secret. Once you have these, you can configure them in e.g. your runtime.exs
like this:
import Config
config :lemon_ex,
api_key: System.get_env("LEMONSQUEEZY_API_KEY"),
webhook_secret: System.get_env("LEMONSQUEEZY_WEBHOOK_SECRET"),
# (Optional) You can provide HTTPoison options which are added to every request.
# See all options here: https://hexdocs.pm/httpoison/HTTPoison.Request.html#content
request_options: [timeout: 10_000]
Alternatively, you can also pass the api_key
as an option when you make a request, like this:
LemonEx.UsageRecords.list([], [api_key: "your-api-key"])
If you don't provide a valid API key, you will receive 401: Unauthorized
error responses.
Every LemonSqueezy object
has its own context
. The contexts implement basic CRUD endpoints where available. Every CRUD endpoint returns either the object as a struct (e.g. LemonEx.Variants.Variant
) or a PaginatedResponse
. For example, the variant
object has the context LemonEx.Variants
which implements a get/1
and list/1
function. You can simply call these functions like so:
iex> LemonEx.Variants.get(variant_id)
{:ok, %LemonEx.Variants.Variant{}}
iex> LemonEx.Variants.list()
{:ok, %LemonEx.PaginatedResponse{data: [%LemonEx.Variants.Variant{}]}}
You can find all defined objects in the LemonSqueezy API docs.
To handle Webhook Events coming from LemonSqueezy, you first have to set up a webhook. You can generate a webhook secret with:
# Generate a webhook signing secret with - by default - 40 characters:
iex> LemonEx.Webhooks.gen_signing_secret()
"ppeSr0unsIMx9ynlilgi0PrekwizK46PjVH/Pi2g"
# Generate a webhook signing secret of 8 characters
iex> LemonEx.Webhooks.gen_signing_secret(8)
"g1NZjc9h"
# Use the generator logic directly, with:
iex> 40 |> :crypto.strong_rand_bytes() |> Base.encode64() |> binary_part(0, 40)
"QgxcfhK39h7PJrfkQkvJ4D2rhwNrfQ1yadjb996q"
Once you set up your webhook, you need to create a module that will handle the events. Like this for example:
defmodule MyAppWeb.MyWebhookHandler do
@behaviour LemonEx.Webhooks.Handler
@impl true
def handle_event(%LemonEx.Webhooks.Event{name: "order_created"} = event) do
# The event.meta holds all provided meta-data, also custom_data.
%{"customer_id" => customer_id} = event.meta["custom_data"]
# The event.data holds the object of the event, like e.g. an `LemonEx.Orders.Order{}`.
new_order = event.data
# do something with the new order
# Return either :ok or {:error, error_message}
:ok
end
# You need to handle all incoming events. So, better have a
# catch-all handler for events that you don't want to handle,
# but only want to acknowledge.
@impl true
def handle_event(_unhandled_event), do: :ok
end
Next, you have to add the LemonEx.Webhooks.Plug
to your endpoint.ex
like this:
# In your endpoint.ex
plug LemonEx.Webhooks.Plug,
at: "/webhook/lemonsqueezy", # <- At which path the Plug expects to receive webhooks
handler: MyAppWeb.MyWebhookHandler # <- Your handler module
# Make sure that this plug comes after the LemonEx plug.
plug Plug.Parsers
And that's it!
When fetching all elements using list/1
, you can add an optional filter, like this:
# Without any filters
LemonEx.Customers.list()
# With both, email and store_id filter
LemonEx.Customers.list(filter: [email: "foo@bar.com", store_id: 12345])
# With only email filter or only store_id filter
LemonEx.Customers.list(filter: [email: "foo@bar.com"])
LemonEx.Customers.list(filter: [store_id: 12345])
You can also provide page: [size: 2, number: 3]
and a sort
option when listing a record, like this:
# With both, email and store_id filter
LemonEx.Customers.list(page: [size: 2, number: 3], sort: "-createdAt")
# You can also combine `page` and `filter` in a single call
LemonEx.Customers.list(filter: [store_id: 12345], page: [size: 2, number: 3], sort: "-createdAt,name")
You can use ngrok to proxy webhook events to your localhost:4000
like this:
- Install
ngrok
with e.g.brew install --cask ngrok
- Start
ngrok
withngrok http 4000
- Copy the URL behind
Forwarding
that looks like this:https://{identifier_here}.eu.ngrok.io
- Create a new Webhook in LemonSqueezy that points to the copied URL plus your endpoint. For example:
https://{identifier}.eu.ngrok.io/webhooks/lemonsqueezy
- Start your Phoenix application and enjoy!
- (Optional): View the event payload in the
Ngrok Inspector
at http://localhost:4040
You can use the testing.livemd
Livebook to test LemonEx endpoints locally. Simply start the Livebook, add your test api key as LEMON_EX_API_KEY
to the livebook, and start testing.
- Add all schema objects
- Add optional filters to requests
- Add webhook plug and handler
- Write better docs
- Allow fetching the next and previous page through
PaginatedResponse
- Allow drop-in of other HTTP libraries
- Add tests
- Add links to API documentation to each schema and context
- Add relationships to schemas
- The timestamps are mostly snake case but sometimes camelCase (e.g. File or User object)
- The API Responses sometimes return foreign IDs as strings, but expect the same IDs to be an integer when making a request to the API. See #3