Shopify + Home Assistant: Unlocking a Cabinet on Order Payment
How I connected a Shopify store to Home Assistant so a paid order automatically unlocks a physical cabinet — replacing a vending machine for under $50 in hardware.
A pilates studio sells accessories and supplements — resistance bands, supplements, water bottles. The instructor is mid-class when a client wants to buy something. Either the class gets interrupted, the sale is skipped, or the products sit behind a counter nobody is attending. None of those are good options.
The brief I gave myself: client pays on their phone, cabinet opens, client takes the product, door closes and relocks. No staff involvement. No vending machine.
Here is how it works.
The Hardware
The cabinet is a standard wooden retail display cabinet. Added to it:
- Electromagnetic lock — energized means locked. Cut the power and it releases. These are cheap, reliable, and reversible. No drilling into mechanisms, no custom mounts.
- Smart switch / relay — controls 12V power to the lock. Exposed to Home Assistant as a
switchentity. - Door/window sensor — a basic Zigbee contact sensor on the door frame. Used by a second automation to re-engage the lock once the door is closed again.
Total hardware cost: under $50.
The Shopify Side
Shopify has native webhook support under Settings → Notifications → Webhooks. Create a webhook for the orders/paid event and point it at your Home Assistant instance:
https://your-ha-instance/api/webhook/-yNvZjEjm6TcI_8fiTaVQrIlM
The webhook ID in the URL is generated by HA when you create the webhook trigger. Shopify will POST the full order object as JSON to that URL every time a payment clears.
The Home Assistant Automation
alias: Shopify Open Cabinet
triggers:
- trigger: webhook
allowed_methods:
- POST
- PUT
local_only: false
webhook_id: "-yNvZjEjm6TcI_8fiTaVQrIlM"
conditions:
- condition: template
value_template: "{{ trigger.json.financial_status == 'paid' }}"
actions:
- action: input_text.set_value
data:
value: "{{ trigger.json.order_number }}"
target:
entity_id: input_text.last_order
- action: input_text.set_value
data:
value: "{{ trigger.json.financial_status }}"
target:
entity_id: input_text.last_order_status
- action: input_text.set_value
data:
value: "{{ trigger.json.total_price }}"
target:
entity_id: input_text.last_order_amount
- action: input_text.set_value
data:
value: >
{% for item in trigger.json.line_items %}
{{ item.title }} (Qty: {{ item.quantity }}, Price: {{ item.price }})
{% endfor %}
target:
entity_id: input_text.last_order_items
- action: switch.turn_off
target:
device_id: de16579e4c0f720d8265f62b935d0145
mode: singleBreaking it down
Trigger — A webhook trigger with local_only: false accepts requests from the public internet, which is required for Shopify to reach HA. The webhook ID acts as a secret; keep it long and random.
Condition — Shopify sends webhooks for several order events. The financial_status == 'paid' check ensures the cabinet only opens when money has actually cleared — not on order creation, not on pending payment.
Actions 1–4 — Four input_text helpers capture the order number, status, total, and a formatted list of line items. These are useful for a dashboard display in the studio (you can show the last order on a wall tablet) and for basic audit logging without a separate database.
Action 5 — switch.turn_off cuts power to the electromagnetic lock. The lock releases, the spring-loaded door mechanism allows it to be pulled open. The client takes their product.
Mode: single — If two orders come in within seconds of each other, only one unlock runs at a time. The cabinet should be open to one customer at a time anyway.
Re-locking the Door
The automation above only handles the unlock. Re-locking is a separate automation triggered by the door sensor:
alias: Cabinet Re-lock on Door Close
triggers:
- trigger: state
entity_id: binary_sensor.cabinet_door
to: "off" # contact sensor: off = closed
actions:
- delay: "00:00:02"
- action: switch.turn_on
target:
device_id: de16579e4c0f720d8265f62b935d0145
mode: singleThe 2-second delay gives the door time to fully seat before the lock engages, preventing the magnet from catching while the door is still swinging.
The Dashboard
A simple Home Assistant Lovelace card on a wall-mounted tablet shows:
- Last order number
- Items purchased
- Total amount
- Lock status (open / locked)
The instructor can glance at it between sets and confirm the last transaction without touching anything.
Cost vs. Vending Machine
| Vending Machine | This Setup | |
|---|---|---|
| Hardware cost | $3,000–5,000+ | ~$50 |
| Payment processing | Proprietary, fixed margin | Shopify (existing) |
| Inventory management | Separate system | Shopify (existing) |
| Receipts & history | Machine memory | Shopify orders |
| Customizable | Rarely | Fully |
The studio already had Shopify for online sales. The only new cost was the lock, relay, and sensor.
Gotchas
Shopify webhook retries. If HA doesn't respond with a 2xx within a few seconds, Shopify retries. HA's webhook trigger responds immediately with 200 before running actions, so this is fine in practice — but be aware that a very slow action chain could cause duplicate triggers. mode: single limits the damage.
HA needs to be reachable from the internet. Either via Nabu Casa, a reverse proxy, or a VPN with port forwarding. If you're exposing HA directly, make sure your instance is up to date and the webhook ID is not guessable.
Electromagnetic locks need clean 12V. Noise on the line can cause chatter. Use a dedicated power supply for the lock, not a shared rail with other devices.
The whole thing took an afternoon to wire up and configure. The instructor hasn't had to leave a class to sell something since.