> ## Documentation Index
> Fetch the complete documentation index at: https://storekit.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Feuille de rapprochement des virements

> Suivez chaque virement storekit dans un registre indexé par svix-id, avec case rapproché et export CSV, alimenté par le webhook payments.payout.created.

Chaque virement que storekit envoie sur votre banque devient une ligne dans votre propre registre au moment où il est créé : montant, devise, compte bancaire concerné et date d'arrivée prévue. Cochez chaque ligne à mesure qu'elle apparaît sur votre relevé bancaire, et téléchargez le mois en CSV pour votre comptable.

> [!TIP]
>
> Le tableau de bord liste les virements sous **Analytics** → **Accounting** → **Payouts** (voir [Comment fonctionnent les virements](/docs/fr/payouts/overview)). Construisez cette recette lorsque vous voulez votre propre état de rapprochement (coché, notes, prévu-vs-arrivé), un export au format attendu par votre logiciel de comptabilité, ou une notification Slack quand un virement est dû.

## Fonctionnement

1. storekit envoie `payments.payout.created` à votre application lorsqu'un virement est créé.
2. Votre application ajoute une ligne par virement avec le total par devise, les quatre derniers chiffres du compte bancaire et la date d'arrivée estimée.
3. Vous cochez les lignes au fur et à mesure sur votre relevé bancaire ; tout ce qui a dépassé sa date d'arrivée sans être coché est mis en évidence.

## Ce dont vous avez besoin

* Les webhooks activés sur votre forfait storekit (**Settings** → **Developers**).
* Un compte [Replit](https://replit.com) — le registre a besoin d'une base de données et d'une URL qui reste en ligne.
* Facultatif : une URL d'incoming webhook Slack pour un rappel hebdomadaire « toujours pas rapproché ».

## Étape 1 : construire l'application

Collez l'intégralité du bloc dans le builder en un seul message.

[![Open in Replit](https://replit.com/badge?caption=Open%20in%20Replit)](https://replit.com/?stack=Build\&prompt=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-VCHtaurlZpSS4S02QHJeC8DBEuJ0TziRYCaD-RYuDlEYLuWCvFcZki0ykDKL4pg471wLHgp8rBkEamWTAZSnc6AOS4MssJLYhWzHTrQy+UAFAYGSCJSa0YhnIEQAAbQALqJA1QwmUMSQAqqRjAgIDEc6jXiiIRKxoHUyDcfSeYaVoRWHtY6ogMB6QKGQDE4QBZijCB3uATlSB37unKV+C4TIaLzHdIsOSClYZ8USGpcosg0AiAKhOM44EzggGgihAiUFLLpuUpm1S2Fi3QQqgRItJaS1QVKuW6Y1N8hWAdqpAAzHWlt0FbDyXLRyB2lxa32Hra2t4bQCIwClBsGd0EanoAIpsqOK6oIZiwBurZ06h24WZW0tgG6ACc57D0ttwl0uwvT+loAIpXbdpzznlpBUSwdN7-7MGpbShY5bI6qSVoiFWDtETOyaA7FWY4lZOzHGrAAWt+1tpRoDdCfQhKC9NLioegrrf1gahAEQtXU7dVa+AchgEBlWIGwMQagzBscfbYPIfw8e0FSCAMkFo-R8DkGnbQYdohtjKsUPXtbRqxA5bSQtiEuoGJGAONeq1IlJtK7W3B1UG6wD2HdwiHoFAfDraPQ6fdR+kQvHJM3oCGFctBlDNQAAAK0STTwJ1yATMEbqN0ctABqJWSte19vgwANgAOwAA5z3eagqgROcN7TUiYgkBdS6V2kRs220gOqCK6s04RQrUmLPYY1rGfgUh8ABjXHgAAXnViA3nW3TNgLp3jZWKtVcVM1gj+B1gEV7VuodR76rBmVINmzR73QKFI6q8jI2f0DGALwObGAps3oDDRUkcQN0vsW1JgJa2FsHfixMu6s18vFZbZBKCbC9PQQTYuhApQ2C6Gwn1-W7Xy3UikCCazn2BsIWdpXaCY3YATeBzhGbx3ofLdWwhMjOEts7dIHtsi139WFdIiu-V2WUodaK4tttCOQAhaVhtu7pXoJa2qv6QM304KU8QGgBQ-By0mERGCAMQYmeFaggVMl6oNMHegqoDQcMHblpCyAcrNYPm9aguLtgMBa3YcVz4GAshy3klNksRXutVApdHdh2mDsORk45ByDX96kHluJMKG3G1oCyG+2V0wNVYi87gOSGAq2NtofqHJUIcQZMIVux6DMc8TcgCdg7RIWyIsU+grm4G2gFAx7A4kXt0WMeLZx0OrL27CUDRJULsJIuRtK8XSrqX2HwsgFpBASwHJehxeV3DNX0FWmR-LnFzX2vsO6-0PrynhuYDG8J1BJkDsQBhY5PSfvtvB-QQd3F6TsP+e2GmCH0gYfCIp5ltHwnceE-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-VGG6B6DWPSQqXbI8DAFRPkusIAa2V-XfLIIGo-U4EeA0KwOwC2B8MfJAHMtCRCMBIEFYDwNALVS4I+dmpUHWaXX3YwJaMsFQzYtVvSmQPvYfd7VPU8ckVSE7M-AxKwo9DUJoHghoCSW5AhJmOTOSByNSKzLhhFazBeNRboKHOgqYBrKbDdUwCrOeqJrdfdY9c9dgMPNBAhercWbUtaA6CyPWeqXDBA7A1A6IJUiNCiYEIhB-VhFfcICJC3VDMY18I1fWGmN0B7FoLQAkJkhUsghIE+ATA9FHXeGSXY1SGFizWzc5NNEbWUCLHABoCVa7eVdGLdg0OlqlGRInTNS2BUroOpdckyK0hLWKMAIeLSK0iANcs9RcFOLqiAPqrQTkKIcmkAbvW7ZIaNA5JNNfd7XbasFDBTiBmKVlWkVuI8MA-AmA5gJxSCXRhcVVOs5s9QoqmQAJCXoieZKsAnZRvGTfRWViV85yDGDMPtGwiVTnXDACK7ZlUVVMHJBrSM+vZCxs6gliw6I+DkAHG8GMh4imv0ARaNHOXFbMLBS4PMQhbxX+XACqsRsIPqeGlBNsdFsxVrn4IDYiPucIEGZ87i7SgAi2H7dE+FCNeYpVRHTVQFbCwCECL0Nabnfs9GFovThCKK98zkAo6IIvOYl-PKEYm+CrvczuNgzJay0GoRSAFCxbo2UdSdeMmXRI1wDo39fwLXdCPXPPWTDUy3ZVu3RAU-BcJpdpbQbSCAH4cAFQbLhQZpdQSAAAJo8ichChLMrNnAmXUJYBogYhWADi4EkgUh6iABkBBZYOCgXAdoF+Y3N8HwC6eW5SBtc0Ns1ANjEsITZUyrYYM4HY3qEFVTKFZY8zKzOzJzGzY4KLeLZLdLbLfLaSNco4HdQ9U9fSEyFfZo0sLGQSKY+Y2FYzJO9TBeI2xGoicWY2BkFIIECiOPfkjAL6MgEzckCllAMHCRawgIne2lO0LENXUAiBoBMDdac8DnGcEAA\&referrer=docs.storekit.com)

[![](https://mintcdn.com/storekit/IGeB_84apyho3Z6U/images/logos/lovable.svg?fit=max\&auto=format\&n=IGeB_84apyho3Z6U\&q=85\&s=cc21d11769d5e6b686fed56390a88f5e) Build with Lovable](https://lovable.dev/#prompt=I%20want%20a%20small%20private%20web%20app%20that%20keeps%20a%20ledger%20of%20the%20payouts%20storekit%20sends%20to%20my%20bank%20account%2C%20so%20I%20can%20reconcile%20them%20against%20my%20bank%20statement.%20storekit%20tells%20the%20app%20about%20each%20payout%20by%20webhook.%20The%20app%20must%20never%20lose%20a%20payout%2C%20never%20record%20the%20same%20one%20twice%2C%20and%20never%20make%20storekit%20wait.%0A%0ABuild%20it%20with%20a%20database%20so%20the%20ledger%20survives%20restarts%20and%20an%20always-on%20public%20HTTPS%20URL.%0A%0ACONTEXT%20ABOUT%20STOREKIT%0A%0AI%20run%20a%20hospitality%20business%20on%20storekit%20\(storekit.com\)%2C%20an%20online%20ordering%20and%20payments%20platform%20for%20restaurants%2C%20caf%C3%A9s%20and%20bars.%20storekit%20can%20notify%20my%20own%20app%20about%20things%20that%20happen%20in%20my%20store%20by%20sending%20webhooks.%0A%0AHow%20storekit%20webhooks%20work%3A%0A-%20Each%20event%20is%20an%20HTTP%20POST%20with%20a%20JSON%20body%20to%20a%20URL%20I%20register%20in%20the%20storekit%20dashboard.%0A-%20The%20body%20has%20the%20shape%20%7B%20%22event%22%3A%20%22%3Cevent%20type%3E%22%2C%20%22data%22%3A%20%7B%20...%20%7D%20%7D.%20Two%20exceptions%3A%20the%20legacy%20order.created%20event%20\(do%20not%20use%20it%3B%20use%20order.created.v2\)%20sends%20the%20bare%20order%20object%2C%20and%20the%20order.refund.created%20%2F%20order.refund.succeeded%20%2F%20order.refund.failed%20events%20put%20order%2C%20customer%2C%20venue%20and%20refund%20next%20to%20event%20instead%20of%20inside%20data.%0A-%20Requests%20are%20signed%20with%20Svix.%20Every%20request%20carries%20the%20headers%20svix-id%2C%20svix-timestamp%20and%20svix-signature.%20Verify%20them%20with%20the%20official%20%60svix%60%20library%20for%20your%20language%2C%20using%20a%20secret%20I%20will%20provide%20in%20an%20environment%20variable%20called%20STOREKIT_WEBHOOK_SECRET.%20Reject%20anything%20that%20fails%20verification%20with%20HTTP%20400.%0A-%20My%20endpoint%20must%20respond%20with%20a%202xx%20status%20within%2015%20seconds.%20Save%20the%20event%20to%20the%20database%20first%2C%20respond%20200%2C%20then%20do%20the%20work%20from%20the%20saved%20copy.%0A-%20Once%20I%20have%20responded%20200%2C%20storekit%20will%20not%20send%20that%20event%20again%2C%20so%20the%20work%20must%20not%20be%20lost%3A%20store%20each%20event%20with%20a%20status%20\(pending%2C%20done%2C%20failed\)%20and%20only%20mark%20it%20done%20after%20the%20work%20has%20actually%20succeeded.%20On%20startup%20and%20on%20a%20timer%2C%20pick%20up%20anything%20still%20pending%20or%20failed%20and%20retry%20it.%20Never%20do%20the%20work%20only%20in%20memory%20after%20responding.%0A-%20The%20same%20event%20can%20occasionally%20be%20delivered%20more%20than%20once.%20Use%20the%20svix-id%20header%20as%20the%20unique%20key%20for%20the%20saved%20event%3A%20a%20repeat%20of%20an%20event%20already%20saved%20is%20ignored%2C%20whatever%20its%20status.%0A-%20Any%20page%20or%20endpoint%20protected%20by%20a%20password%20must%20be%20served%20over%20HTTPS%20only%3B%20redirect%20or%20refuse%20plain%20HTTP.%0A-%20Money%20fields%20\(total%2C%20tip%2C%20deliveryFee%2C%20discountTotal%2C%20item%20price%2C%20modifier%20price\)%20are%20integers%20in%20minor%20units%3A%202500%20means%20%C2%A325.00.%0A-%20orderType%20is%20one%20of%20Delivery%2C%20Pickup%2C%20InStore%2C%20Curbside%2C%20CateringDelivery%2C%20CateringPickup%20or%20Billpay.%0A-%20table%20is%20only%20present%20for%20in-store%20orders%3B%20deliveryAddress%20and%20pickUpTime%20only%20for%20delivery%20orders.%20When%20they%20do%20not%20apply%20the%20key%20is%20omitted%2C%20so%20treat%20a%20missing%20key%20the%20same%20as%20null.%20notes%20and%20items%5B%5D.note%20can%20be%20null%20or%20empty.%20customer.companyName%20and%20customer.vatNumber%20appear%20only%20when%20supplied.%0A%0AExample%20order.created.v2%20payload%3A%0A%0A%7B%0A%20%20%22event%22%3A%20%22order.created.v2%22%2C%0A%20%20%22data%22%3A%20%7B%0A%20%20%20%20%22id%22%3A%20%22ord_abc123%22%2C%0A%20%20%20%20%22code%22%3A%20%22A1B2%22%2C%0A%20%20%20%20%22asap%22%3A%20true%2C%0A%20%20%20%20%22total%22%3A%202500%2C%0A%20%20%20%20%22tip%22%3A%20250%2C%0A%20%20%20%20%22deliveryFee%22%3A%20299%2C%0A%20%20%20%20%22discountTotal%22%3A%200%2C%0A%20%20%20%20%22orderType%22%3A%20%22Delivery%22%2C%0A%20%20%20%20%22pickUpTime%22%3A%20%222024-01-15T10%3A45%3A00Z%22%2C%0A%20%20%20%20%22channel%22%3A%20%22WEB%22%2C%0A%20%20%20%20%22poNumber%22%3A%20null%2C%0A%20%20%20%20%22createdAt%22%3A%20%222024-01-15T10%3A30%3A00Z%22%2C%0A%20%20%20%20%22deliveryTime%22%3A%20%222024-01-15T11%3A00%3A00Z%22%2C%0A%20%20%20%20%22notes%22%3A%20%22Ring%20doorbell%22%2C%0A%20%20%20%20%22customer%22%3A%20%7B%0A%20%20%20%20%20%20%22firstName%22%3A%20%22John%22%2C%0A%20%20%20%20%20%20%22lastName%22%3A%20%22Doe%22%2C%0A%20%20%20%20%20%20%22email%22%3A%20%22john%40example.com%22%2C%0A%20%20%20%20%20%20%22phone%22%3A%20%22%2B44123456789%22%2C%0A%20%20%20%20%20%20%22marketingConsent%22%3A%20true%0A%20%20%20%20%7D%2C%0A%20%20%20%20%22items%22%3A%20%5B%0A%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%22name%22%3A%20%22Margherita%20Pizza%22%2C%0A%20%20%20%20%20%20%20%20%22printName%22%3A%20%22Margherita%22%2C%0A%20%20%20%20%20%20%20%20%22price%22%3A%201200%2C%0A%20%20%20%20%20%20%20%20%22quantity%22%3A%201%2C%0A%20%20%20%20%20%20%20%20%22plu%22%3A%20null%2C%0A%20%20%20%20%20%20%20%20%22posId%22%3A%20null%2C%0A%20%20%20%20%20%20%20%20%22taxRate%22%3A%2020%2C%0A%20%20%20%20%20%20%20%20%22note%22%3A%20null%2C%0A%20%20%20%20%20%20%20%20%22modifiers%22%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%7B%20%22name%22%3A%20%22Extra%20cheese%22%2C%20%22printName%22%3A%20%22Cheese%22%2C%20%22price%22%3A%20150%2C%20%22quantity%22%3A%201%2C%20%22plu%22%3A%20null%2C%20%22posId%22%3A%20null%2C%20%22taxRate%22%3A%2020%20%7D%0A%20%20%20%20%20%20%20%20%5D%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%5D%2C%0A%20%20%20%20%22venue%22%3A%20%7B%0A%20%20%20%20%20%20%22id%22%3A%201234%2C%0A%20%20%20%20%20%20%22name%22%3A%20%22My%20Restaurant%22%2C%0A%20%20%20%20%20%20%22slug%22%3A%20%22my-restaurant%22%2C%0A%20%20%20%20%20%20%22address%22%3A%20%7B%0A%20%20%20%20%20%20%20%20%22street1%22%3A%20%22123%20Main%20St%22%2C%0A%20%20%20%20%20%20%20%20%22street2%22%3A%20%22%22%2C%0A%20%20%20%20%20%20%20%20%22city%22%3A%20%22London%22%2C%0A%20%20%20%20%20%20%20%20%22postCode%22%3A%20%22W1A%201AA%22%2C%0A%20%20%20%20%20%20%20%20%22country%22%3A%20%22UK%22%2C%0A%20%20%20%20%20%20%20%20%22companyName%22%3A%20%22My%20Restaurant%20Ltd%22%2C%0A%20%20%20%20%20%20%20%20%22coordinates%22%3A%20%7B%20%22latitude%22%3A%2051.5074%2C%20%22longitude%22%3A%20-0.1278%20%7D%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%2C%0A%20%20%20%20%22deliveryAddress%22%3A%20%7B%0A%20%20%20%20%20%20%22street1%22%3A%20%22456%20Oak%20Ave%22%2C%0A%20%20%20%20%20%20%22street2%22%3A%20%22Flat%202%22%2C%0A%20%20%20%20%20%20%22city%22%3A%20%22London%22%2C%0A%20%20%20%20%20%20%22postCode%22%3A%20%22E1%206AN%22%2C%0A%20%20%20%20%20%20%22country%22%3A%20%22UK%22%2C%0A%20%20%20%20%20%20%22notes%22%3A%20null%2C%0A%20%20%20%20%20%20%22coordinates%22%3A%20%7B%20%22latitude%22%3A%2051.5155%2C%20%22longitude%22%3A%20-0.0722%20%7D%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D%0A%0AThe%20full%20list%20of%20event%20types%20and%20payloads%20is%20at%20https%3A%2F%2Fstorekit.com%2Fdocs%2Fdevelopers%2Fwebhooks%2Fwebhook-events%0A%0ATHE%20EVENT%20THIS%20APP%20USES%3A%20payments.payout.created%0A%0AIgnore%20the%20order%20example%20above%3B%20this%20app%20only%20cares%20about%20payments.payout.created%2C%20which%20looks%20like%20this%3A%0A%0A%7B%0A%20%20%22event%22%3A%20%22payments.payout.created%22%2C%0A%20%20%22data%22%3A%20%7B%0A%20%20%20%20%22amounts%22%3A%20%5B%0A%20%20%20%20%20%20%7B%20%22currency%22%3A%20%22GBP%22%2C%20%22value%22%3A%20%223210.50%22%20%7D%2C%0A%20%20%20%20%20%20%7B%20%22currency%22%3A%20%22GBP%22%2C%20%22value%22%3A%20%221030.00%22%20%7D%0A%20%20%20%20%5D%2C%0A%20%20%20%20%22bankAccount%22%3A%20%7B%0A%20%20%20%20%20%20%22id%22%3A%20%22db97e205-105c-42ff-8460-d06c25cb6830%22%2C%0A%20%20%20%20%20%20%22IBAN%22%3A%20%22GB15HBUK40127612345678%22%2C%0A%20%20%20%20%20%20%22accountNumber%22%3A%20%220001234%22%2C%0A%20%20%20%20%20%20%22branchCode%22%3A%20%22001234%22%2C%0A%20%20%20%20%20%20%22currency%22%3A%20%22GBP%22%0A%20%20%20%20%7D%2C%0A%20%20%20%20%22estimatedArrivalDate%22%3A%20%221%2F2024%22%0A%20%20%7D%0A%7D%0A%0AImportant%20differences%20from%20the%20order%20events%3A%0A-%20Money%20here%20is%20NOT%20in%20minor%20units.%20Each%20value%20is%20a%20decimal%20string%20in%20major%20units%3A%20%223210.50%22%20means%203%2C210.50.%20Parse%20it%20as%20a%20decimal%2C%20never%20as%20a%20float%20you%20then%20round%20%E2%80%94%20use%20a%20decimal%20library%20or%20store%20it%20as%20a%20string%20and%20sum%20with%20integer%20pence.%0A-%20amounts%20is%20a%20list.%20Sum%20the%20values%20per%20currency%3B%20a%20payout%20usually%20has%20one%20currency%20but%20do%20not%20assume%20it.%20Show%20each%20currency%20total%20on%20the%20row.%0A-%20The%20payload%20has%20no%20payout%20id%20and%20no%20store%20name.%20Use%20the%20svix-id%20header%20as%20the%20unique%20key%20for%20the%20row.%0A-%20bankAccount%20contains%20full%20bank%20details.%20Store%20only%20the%20last%20four%20characters%20of%20accountNumber%20\(or%20of%20IBAN%20if%20accountNumber%20is%20missing\)%20and%20the%20bankAccount.id.%20Do%20not%20store%20or%20display%20the%20full%20IBAN%20or%20account%20number%20anywhere%2C%20including%20logs.%0A-%20estimatedArrivalDate%20is%20a%20%22month%2Fyear%22%20string%20such%20as%20%221%2F2024%22%2C%20or%20an%20empty%20string%20when%20unknown.%20Show%20it%20as%20text%3B%20do%20not%20parse%20it%20as%20a%20date.%0A%0AWHAT%20TO%20BUILD%0A%0A1.%20POST%20%2Fwebhooks%2Fstorekit%20%E2%80%94%20verify%20the%20Svix%20signature%20with%20the%20official%20svix%20library%20and%20STOREKIT_WEBHOOK_SECRET%20against%20the%20raw%20request%20body%3B%20400%20on%20failure.%20Save%20the%20row%20and%20return%20200%20immediately.%20Ignore%20any%20svix-id%20already%20saved.%20Handle%20payments.payout.created%3B%20save%20and%20acknowledge%20any%20other%20event%20type%20but%20do%20nothing%20with%20it.%0A2.%20A%20ledger%20table%20with%3A%20received%20time%20\(UTC%2C%20display%20in%20TIMEZONE\)%2C%20amounts%20per%20currency%2C%20bank%20account%20last%20four%2C%20bank%20account%20id%2C%20estimated%20arrival%20date%2C%20reconciled%20\(yes%2Fno\)%2C%20reconciled%20on%2C%20statement%20reference%20\(free%20text\)%2C%20notes%20\(free%20text\).%0A3.%20GET%20%2F%20%E2%80%94%20protected%20by%20HTTP%20basic%20auth%20with%20ADMIN_PASSWORD.%20A%20table%20of%20payouts%20newest%20first%20with%20a%20month%20filter%20\(default%3A%20current%20month\)%2C%20showing%20the%20columns%20above.%20Each%20row%20has%20a%20Reconciled%20tick-box%20and%20editable%20statement%20reference%20and%20notes%20fields%20that%20save%20without%20a%20page%20reload.%20Rows%20received%20more%20than%207%20days%20ago%20and%20not%20reconciled%20are%20highlighted%20amber.%20Above%20the%20table%3A%20total%20paid%20out%20this%20month%20per%20currency%2C%20total%20reconciled%2C%20total%20outstanding.%0A4.%20Download%20CSV%20for%20the%20filtered%20month%2C%20one%20row%20per%20payout%2C%20columns%3A%20received_date%2C%20currency%2C%20amount%2C%20bank_last4%2C%20estimated_arrival%2C%20reconciled%2C%20reconciled_on%2C%20statement_reference%2C%20notes.%20Amounts%20as%20plain%20decimals%20with%20two%20places%20and%20no%20currency%20symbol%20so%20bookkeeping%20software%20imports%20them%20cleanly.%20If%20a%20payout%20has%20more%20than%20one%20currency%2C%20write%20one%20CSV%20row%20per%20currency.%0A5.%20Optional%20weekly%20reminder%3A%20if%20SLACK_WEBHOOK_URL%20is%20set%2C%20every%20Monday%20at%2009%3A00%20in%20TIMEZONE%20post%20%22N%20payouts%20totalling%20X%20not%20yet%20reconciled%2C%20oldest%20from%20%3Cdate%3E%22%2C%20or%20nothing%20if%20there%20are%20none.%20Skip%20entirely%20if%20the%20variable%20is%20unset.%0A6.%20GET%20%2Fhealth%20that%20returns%20200%20and%20%7B%20%22ok%22%3A%20true%20%7D%20and%20nothing%20else.%0A%0ADEFINITION%20OF%20DONE%0A%0A-%20%5B%20%5D%20The%20example%20payload%20above%20creates%20one%20row%20showing%204%2C240.50%20GBP%20with%20estimated%20arrival%20%221%2F2024%22.%0A-%20%5B%20%5D%20The%20same%20event%20delivered%20twice%20creates%20one%20row.%0A-%20%5B%20%5D%20A%20bad%20signature%20returns%20400%20and%20records%20nothing.%0A-%20%5B%20%5D%20Nothing%20in%20the%20database%2C%20page%20or%20logs%20contains%20the%20full%20IBAN%20or%20account%20number%20%E2%80%94%20only%20%225678%22%20style%20last-four%20values.%0A-%20%5B%20%5D%20Ticking%20Reconciled%20saves%20immediately%20and%20survives%20a%20reload%20and%20a%20restart.%0A-%20%5B%20%5D%20The%20CSV%20opens%20in%20a%20spreadsheet%20with%20amounts%20as%20numbers.%0A-%20%5B%20%5D%20A%20payout%20received%20more%20than%20a%20week%20ago%20and%20not%20reconciled%20is%20highlighted.%0A%0AWHAT%20TO%20GIVE%20ME%20WHEN%20YOU%20ARE%20DONE%0A%0A1.%20The%20public%20webhook%20URL%20\(%E2%80%A6%2Fwebhooks%2Fstorekit\)%20and%20the%20ledger%20URL.%0A2.%20The%20environment%20variables%20to%20set%20\(STOREKIT_WEBHOOK_SECRET%2C%20ADMIN_PASSWORD%2C%20TIMEZONE%2C%20optionally%20SLACK_WEBHOOK_URL\)%20and%20where%20to%20set%20them%20on%20this%20platform.%0A3.%20Confirmation%20that%20the%20app%20stays%20up%2024%20hours%20a%20day.%0A)

Either button opens the builder with the whole prompt below already filled in. Nothing starts until you press send, so read it through and edit the parts marked as yours first.

> **Prompt complet (cliquez pour développer)**
>
> ```text theme={null}
> I want a small private web app that keeps a ledger of the payouts storekit sends to my bank account, so I can reconcile them against my bank statement. storekit tells the app about each payout by webhook. The app must never lose a payout, never record the same one twice, and never make storekit wait.
>
> Build it with a database so the ledger survives restarts and an always-on public HTTPS URL.
>
> ```
>
> ```text theme={null}
> CONTEXT ABOUT STOREKIT
>
> I run a hospitality business on storekit (storekit.com), an online ordering and payments platform for restaurants, cafés and bars. storekit can notify my own app about things that happen in my store by sending webhooks.
>
> How storekit webhooks work:
> - Each event is an HTTP POST with a JSON body to a URL I register in the storekit dashboard.
> - The body has the shape { "event": "<event type>", "data": { ... } }. Two exceptions: the legacy order.created event (do not use it; use order.created.v2) sends the bare order object, and the order.refund.created / order.refund.succeeded / order.refund.failed events put order, customer, venue and refund next to event instead of inside data.
> - Requests are signed with Svix. Every request carries the headers svix-id, svix-timestamp and svix-signature. Verify them with the official `svix` library for your language, using a secret I will provide in an environment variable called STOREKIT_WEBHOOK_SECRET. Reject anything that fails verification with HTTP 400.
> - My endpoint must respond with a 2xx status within 15 seconds. Save the event to the database first, respond 200, then do the work from the saved copy.
> - Once I have responded 200, storekit will not send that event again, so the work must not be lost: store each event with a status (pending, done, failed) and only mark it done after the work has actually succeeded. On startup and on a timer, pick up anything still pending or failed and retry it. Never do the work only in memory after responding.
> - The same event can occasionally be delivered more than once. Use the svix-id header as the unique key for the saved event: a repeat of an event already saved is ignored, whatever its status.
> - Any page or endpoint protected by a password must be served over HTTPS only; redirect or refuse plain HTTP.
> - Money fields (total, tip, deliveryFee, discountTotal, item price, modifier price) are integers in minor units: 2500 means £25.00.
> - orderType is one of Delivery, Pickup, InStore, Curbside, CateringDelivery, CateringPickup or Billpay.
> - table is only present for in-store orders; deliveryAddress and pickUpTime only for delivery orders. When they do not apply the key is omitted, so treat a missing key the same as null. notes and items[].note can be null or empty. customer.companyName and customer.vatNumber appear only when supplied.
>
> Example order.created.v2 payload:
>
> {
>   "event": "order.created.v2",
>   "data": {
>     "id": "ord_abc123",
>     "code": "A1B2",
>     "asap": true,
>     "total": 2500,
>     "tip": 250,
>     "deliveryFee": 299,
>     "discountTotal": 0,
>     "orderType": "Delivery",
>     "pickUpTime": "2024-01-15T10:45:00Z",
>     "channel": "WEB",
>     "poNumber": null,
>     "createdAt": "2024-01-15T10:30:00Z",
>     "deliveryTime": "2024-01-15T11:00:00Z",
>     "notes": "Ring doorbell",
>     "customer": {
>       "firstName": "John",
>       "lastName": "Doe",
>       "email": "john@example.com",
>       "phone": "+44123456789",
>       "marketingConsent": true
>     },
>     "items": [
>       {
>         "name": "Margherita Pizza",
>         "printName": "Margherita",
>         "price": 1200,
>         "quantity": 1,
>         "plu": null,
>         "posId": null,
>         "taxRate": 20,
>         "note": null,
>         "modifiers": [
>           { "name": "Extra cheese", "printName": "Cheese", "price": 150, "quantity": 1, "plu": null, "posId": null, "taxRate": 20 }
>         ]
>       }
>     ],
>     "venue": {
>       "id": 1234,
>       "name": "My Restaurant",
>       "slug": "my-restaurant",
>       "address": {
>         "street1": "123 Main St",
>         "street2": "",
>         "city": "London",
>         "postCode": "W1A 1AA",
>         "country": "UK",
>         "companyName": "My Restaurant Ltd",
>         "coordinates": { "latitude": 51.5074, "longitude": -0.1278 }
>       }
>     },
>     "deliveryAddress": {
>       "street1": "456 Oak Ave",
>       "street2": "Flat 2",
>       "city": "London",
>       "postCode": "E1 6AN",
>       "country": "UK",
>       "notes": null,
>       "coordinates": { "latitude": 51.5155, "longitude": -0.0722 }
>     }
>   }
> }
>
> The full list of event types and payloads is at https://storekit.com/docs/developers/webhooks/webhook-events
> ```
>
> ```text theme={null}
> THE EVENT THIS APP USES: payments.payout.created
>
> Ignore the order example above; this app only cares about payments.payout.created, which looks like this:
>
> {
>   "event": "payments.payout.created",
>   "data": {
>     "amounts": [
>       { "currency": "GBP", "value": "3210.50" },
>       { "currency": "GBP", "value": "1030.00" }
>     ],
>     "bankAccount": {
>       "id": "db97e205-105c-42ff-8460-d06c25cb6830",
>       "IBAN": "GB15HBUK40127612345678",
>       "accountNumber": "0001234",
>       "branchCode": "001234",
>       "currency": "GBP"
>     },
>     "estimatedArrivalDate": "1/2024"
>   }
> }
>
> Important differences from the order events:
> - Money here is NOT in minor units. Each value is a decimal string in major units: "3210.50" means 3,210.50. Parse it as a decimal, never as a float you then round — use a decimal library or store it as a string and sum with integer pence.
> - amounts is a list. Sum the values per currency; a payout usually has one currency but do not assume it. Show each currency total on the row.
> - The payload has no payout id and no store name. Use the svix-id header as the unique key for the row.
> - bankAccount contains full bank details. Store only the last four characters of accountNumber (or of IBAN if accountNumber is missing) and the bankAccount.id. Do not store or display the full IBAN or account number anywhere, including logs.
> - estimatedArrivalDate is a "month/year" string such as "1/2024", or an empty string when unknown. Show it as text; do not parse it as a date.
>
> WHAT TO BUILD
>
> 1. POST /webhooks/storekit — verify the Svix signature with the official svix library and STOREKIT_WEBHOOK_SECRET against the raw request body; 400 on failure. Save the row and return 200 immediately. Ignore any svix-id already saved. Handle payments.payout.created; save and acknowledge any other event type but do nothing with it.
> 2. A ledger table with: received time (UTC, display in TIMEZONE), amounts per currency, bank account last four, bank account id, estimated arrival date, reconciled (yes/no), reconciled on, statement reference (free text), notes (free text).
> 3. GET / — protected by HTTP basic auth with ADMIN_PASSWORD. A table of payouts newest first with a month filter (default: current month), showing the columns above. Each row has a Reconciled tick-box and editable statement reference and notes fields that save without a page reload. Rows received more than 7 days ago and not reconciled are highlighted amber. Above the table: total paid out this month per currency, total reconciled, total outstanding.
> 4. Download CSV for the filtered month, one row per payout, columns: received_date, currency, amount, bank_last4, estimated_arrival, reconciled, reconciled_on, statement_reference, notes. Amounts as plain decimals with two places and no currency symbol so bookkeeping software imports them cleanly. If a payout has more than one currency, write one CSV row per currency.
> 5. Optional weekly reminder: if SLACK_WEBHOOK_URL is set, every Monday at 09:00 in TIMEZONE post "N payouts totalling X not yet reconciled, oldest from <date>", or nothing if there are none. Skip entirely if the variable is unset.
> 6. GET /health that returns 200 and { "ok": true } and nothing else.
>
> DEFINITION OF DONE
>
> - [ ] The example payload above creates one row showing 4,240.50 GBP with estimated arrival "1/2024".
> - [ ] The same event delivered twice creates one row.
> - [ ] A bad signature returns 400 and records nothing.
> - [ ] Nothing in the database, page or logs contains the full IBAN or account number — only "5678" style last-four values.
> - [ ] Ticking Reconciled saves immediately and survives a reload and a restart.
> - [ ] The CSV opens in a spreadsheet with amounts as numbers.
> - [ ] A payout received more than a week ago and not reconciled is highlighted.
>
> WHAT TO GIVE ME WHEN YOU ARE DONE
>
> 1. The public webhook URL (…/webhooks/storekit) and the ledger URL.
> 2. The environment variables to set (STOREKIT_WEBHOOK_SECRET, ADMIN_PASSWORD, TIMEZONE, optionally SLACK_WEBHOOK_URL) and where to set them on this platform.
> 3. Confirmation that the app stays up 24 hours a day.
> ```

## Étape 2 : connecter storekit

**1. Open the webhooks portal**

In the dashboard sidebar, go to **Settings** → **Webhooks**. If your plan includes webhooks and they are not yet enabled, click **Enable Webhooks**. If the page shows **Upgrade to enable webhooks**, [contact support](/docs/getting-started/contact-support) to discuss adding webhooks to your plan.

**2. Add your app as an endpoint**

In the embedded portal, add a new endpoint, paste the webhook URL your app builder gave you, and tick only the event types the recipe needs.

**3. Copy the signing secret into your app**

Open the endpoint you just created and copy its signing secret. In your app builder, save it as the environment variable `STOREKIT_WEBHOOK_SECRET` (builders call this "secrets" or "environment variables"). Redeploy if the builder asks you to.

**4. Send a test event**

Use the portal's test option on the endpoint to send a sample event. The delivery log shows the response code from your app. A `2xx` means your app accepted it; anything else, read the response body in the log — it is usually a missing secret.

Cochez uniquement `payments.payout.created` pour cet endpoint. Utilisez l'option de test du portail avec ce type d'événement pour créer votre première ligne.

## Étape 3 : rapprocher

Quand le prochain virement réel arrive, comparez la ligne à **Analytics** → **Accounting** → **Payouts** dans le tableau de bord et à votre relevé bancaire, puis cochez-la. Les virements suivent votre [calendrier de virements](/docs/fr/payouts/payout-schedule), donc la première ligne réelle peut prendre quelques jours.

## Idées

* **Envoyer automatiquement à votre comptable** : demandez au builder d'envoyer par e-mail le CSV du mois le 1er du mois via un prestataire tel que Resend ou Postmark.
* **Rapprocher par montant** : collez un CSV de relevé bancaire et laissez l'application suggérer des correspondances par montant et par date.
* **Plusieurs magasins** : exécutez un endpoint par compte storekit et ajoutez une étiquette « magasin » à partir d'une variable d'environnement pour que les registres puissent être fusionnés.

## Ce que cela ne fait pas

* **Cela n'explique pas le contenu d'un virement.** L'événement contient les totaux, pas les commandes, les frais ou les remboursements qui le composent ; utilisez le [rapport des frais](/docs/fr/guides/reports/fee-report) et le [rapport des transactions](/docs/fr/guides/reports/transactions-report) pour cela.
* **Cela commence à partir du jour où vous le connectez.** Les virements antérieurs se trouvent dans le tableau de bord, pas dans le flux d'événements.
* **Ce n'est pas une preuve de paiement.** L'événement indique qu'un virement a été créé ; qu'il arrive, et quand, dépend du fournisseur de paiement et de votre banque.
* **C'est votre service.** Le support storekit peut confirmer que l'événement a atteint votre URL et le code de réponse ; le registre et l'export sont les vôtres.

## Voir aussi

> **[Payload payments.payout.created](/docs/fr/developers/webhooks/webhook-events#payments-payout-created)**
>
> Le payload complet, y compris les champs que cette recette ne stocke volontairement pas.

> **[Comment fonctionnent les virements](/docs/fr/payouts/overview)**
>
> Délais, frais et emplacement des virements dans le tableau de bord.

> **[Écrire un prompt qui fonctionne](/docs/fr/guides/build/prompting-guide)**
>
> La structure derrière ce prompt, et quoi dire quand le builder se trompe.

> **[Rapport des frais](/docs/fr/guides/reports/fee-report)**
>
> Ce qui a été déduit avant le virement.


## Related topics

- [Génération des codes de commande](/docs/fr/guides/orders/advanced/order-sequence-policy.md)
- [Calendrier de versement](/docs/fr/payouts/payout-schedule.md)
- [Gérer les commandes de traiteur](/docs/fr/guides/fulfillment/catering-setup.md)
- [Rapports de ventes et de comptabilité dans storekit](/docs/fr/guides/reports/overview.md)
- [Événements webhook](/docs/fr/developers/webhooks/webhook-events.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.