// composer.json
{
"require": {
"php": ">=8.2",
"ext-ctype": "*",
"ext-iconv": "*",
"doctrine/dbal": "^3", // 3.9.4
"doctrine/doctrine-bundle": "^2.13", // 2.13.2
"doctrine/doctrine-migrations-bundle": "^3.4", // 3.4.1
"doctrine/orm": "^3.3", // 3.3.2
"symfony/asset": "7.2.*", // v7.2.0
"symfony/asset-mapper": "7.2.*", // v7.2.3
"symfony/console": "7.2.*", // v7.2.1
"symfony/dotenv": "7.2.*", // v7.2.0
"symfony/flex": "^2", // v2.5.0
"symfony/form": "7.2.*", // v7.2.4
"symfony/framework-bundle": "7.2.*", // v7.2.4
"symfony/http-client": "7.2.*", // v7.2.4
"symfony/runtime": "7.2.*", // v7.2.3
"symfony/security-bundle": "7.2.*", // v7.2.3
"symfony/stimulus-bundle": "^2.22", // v2.23.0
"symfony/twig-bundle": "7.2.*", // v7.2.0
"symfony/validator": "7.2.*", // v7.2.4
"symfony/webhook": "7.2.*", // v7.2.0
"symfony/yaml": "7.2.*", // v7.2.3
"symfonycasts/tailwind-bundle": "^0.7.1", // v0.7.1
"twig/extra-bundle": "^2.12|^3.0", // v3.20.0
"twig/twig": "^2.12|^3.0" // v3.20.0
},
"require-dev": {
"doctrine/doctrine-fixtures-bundle": "^4.0", // 4.0.0
"phpunit/phpunit": "^9.5", // 9.6.22
"symfony/browser-kit": "7.2.*", // v7.2.0
"symfony/css-selector": "7.2.*", // v7.2.0
"symfony/maker-bundle": "^1.62", // v1.62.1
"symfony/phpunit-bridge": "^7.2", // v7.2.0
"symfony/stopwatch": "7.2.*", // v7.2.4
"symfony/web-profiler-bundle": "7.2.*", // v7.2.4
"zenstruck/foundry": "*" // v2.3.8
}
}
8 Comments
I have a problem in the listOrders method in LemonSqueezyApi. The LsCustomerId of the user is not set. Where should it be set?
Hey Francois,
We're setting that lsCustomerId in the
LemonSqueezyWebhookConsumer, see this specific code block: https://symfonycasts.com/screencast/lemon-squeezy/webhook-consumer#codeblock-607e352f6a . Basically, we sync that via the webhook. First of all, make sure the webhook contains user_id so that on our side we could fetch the related User object for the received webhook. Make sure that the user ID didn't change in the DB (because maybe you reload the fixtures that in turn incremented IDs?). If we have user ID, and was able to find the related User in the DB - we will set LS customer ID from the webhook on it. But this requires also ngrok tunnel working, otherwise webhook can't just reach your localhost.Cheers!
Hey @Brontie, could you help to figure out where that lsCustomerId should be set, please?
Assuming you mean in this chapter’s flow,
lsCustomerIdgets set when you assign a Lemon Squeezy customer to your Symfony user after a successful checkout. That happens outsidelistOrders(): the site needs to store the Lemon Squeezy customer ID on yourUserentity when the customer is created or matched, so laterlistOrders()can use it to fetch the canonical Lemon Squeezy customer record.The important part is that
listOrders()only reads$user->getLsCustomerId(); it doesn’t create it. If that field isnull, the method intentionally returns an empty array, which is why the orders section shows “No orders yet” for unlinked users.$lsCustomerId = $user->getLsCustomerId(); if (!$lsCustomerId) { return []; }Tip: don’t try to use the user’s email as the source of truth here — the chapter hardens this specifically so the lookup depends on the Lemon Squeezy customer record, not an editable site email.
Sources:
OK, but where before we set that lsCustomerId, it seems that it happened not in this chapter. How and where that lsCustomerId is set on the User object?
Assuming you mean in this codebase,
lsCustomerIdis not set insidelistOrders()at all — it’s stored on theUserwhen your app first links that user to a Lemon Squeezy customer. In the flow from this chapter, that linkage must already exist from an earlier step or another part of the app: once you have the Lemon Squeezy customer ID, you persist it on theUserentity and save it to the database.listOrders()then relies on$user->getLsCustomerId()to decide whether it can safely look up orders. If that field is missing, the method correctly returns[], because there’s no secure way to know which Lemon Squeezy customer belongs to that user.$lsCustomerId = $user->getLsCustomerId(); if (!$lsCustomerId) { return []; }Common gotcha: if you’re seeing
nullhere, the issue is usually that the user was never linked to a Lemon Squeezy customer in your database, not thatlistOrders()is broken.Sources:
Ok, here's what Claude said about this topic in the finish project of this course.
LsCustomerIdis set in two places:Webhook consumer:
src/RemoteEvent/LemonSqueezyWebhookConsumer.php:45-46. When Lemon Squeezy sends anorder_createdwebhook,handleOrderCreatedEvent()readsdata.attributes.customer_idfrom the payload and calls$user->setLsCustomerId(). It finds the user throughmeta.custom_data.user_id, which is the custom data passed when the checkout URL is created. This is the main, trustworthy path, because the request comes from Lemon Squeezy.Checkout handler:
src/Controller/OrderController.php:92-93. Theapp_order_checkout_handleroute (POST/checkout/handle) takeslsCustomerIdfrom the request body and saves it on the current user. It first checks that the posteduserIdmatches the logged-in user. The front end probably calls it from the Lemon Squeezy overlay's checkout-success event.You probably missed them because
User.phponly has the plain getter and setter. The setting happens in the webhook consumer and the controller, not in the entity.The second path has a weakness.
lsCustomerIdcomes straight from the client, so a logged-in user could post any customer ID.listOrderswould then fetch another customer's orders. Thels_customer_idcolumn also has a unique index, so a duplicate would cause an error. If the webhook already covers this, I'd drop or harden the controller endpoint. For example, it could verify the ID through the API (retrieveCustomer) before saving it. Do you want me to make that change?At this stage of the course, it seems that the checkout handler in the order controller does not set LsCustomerId. So, it must be set in the webhook consumer. BUT, in my tutorial code (currently being updated at this very course), I have ordered several items and the LsCustomerId entry in user is till not set.
Hey Francois,
Have you seen my comment: https://symfonycasts.com/screencast/lemon-squeezy/list-orders#comment-34335
So yes, Claude is right, and that LS customer ID should be set in the
LemonSqueezyWebhookConsumer. So, make sure you have the ngrok tunnel started (keep in mind that when you stop and start it again - the URL is most probably changed, so you have to change it in the LemonSqueezy dashboard as well otherwise it won't be able to reach your server, but you should see it in logs there). Then make sure that that webhook endpoint in your app returns 200 status code and not fatal errors during that process, otherwise the LS customer ID saving may actually never happen. Ideally, use ngrok HTTP inspector to see what exactly data you receive and what was your response, basically I would recommend to rewatch previous chapter: https://symfonycasts.com/screencast/lemon-squeezy/webhook-consumer .So, in short, you need to debug that spot around the setLsCustomerId() call in webhook parser: if it was reached by LS webhook and what exactly data you have there to realize why it waas not set, maybe you're mossing user_id in custom webhook metadata, or it mismatch your local user ID, or are there any fatal error happened during that process.
I hope that helps!
Cheers!
"Houston: no signs of life"
Start the conversation!