[{"data":1,"prerenderedAt":1320},["ShallowReactive",2],{"navigation_docs":3,"-packages-fluent-php-webhooks":465,"-packages-fluent-php-webhooks-surround":1315},[4,8,62,115,450],{"title":5,"path":6,"stem":7},"Introduction","\u002Fintroduction","0.introduction",{"title":9,"path":10,"stem":11,"children":12,"page":61},"Guides","\u002Fguides","1.guides",[13,17,21,25,29,33,37,41,45,49,53,57],{"title":14,"path":15,"stem":16},"Quickstart","\u002Fguides\u002Fquickstart","1.guides\u002F1.quickstart",{"title":18,"path":19,"stem":20},"Payouts & Settlement","\u002Fguides\u002Fpayouts","1.guides\u002F10.payouts",{"title":22,"path":23,"stem":24},"Customer Emails","\u002Fguides\u002Fcustomer-emails","1.guides\u002F11.customer-emails",{"title":26,"path":27,"stem":28},"Build your own integration","\u002Fguides\u002Fbuild-your-own-integration","1.guides\u002F2.build-your-own-integration",{"title":30,"path":31,"stem":32},"Selling Workflow","\u002Fguides\u002Fselling-workflow","1.guides\u002F2.selling-workflow",{"title":34,"path":35,"stem":36},"Vatly SDKs","\u002Fguides\u002Fsdks","1.guides\u002F3.sdks",{"title":38,"path":39,"stem":40},"Authentication","\u002Fguides\u002Fauthentication","1.guides\u002F4.authentication",{"title":42,"path":43,"stem":44},"Pagination","\u002Fguides\u002Fpagination","1.guides\u002F5.pagination",{"title":46,"path":47,"stem":48},"Errors","\u002Fguides\u002Ferrors","1.guides\u002F6.errors",{"title":50,"path":51,"stem":52},"Webhooks","\u002Fguides\u002Fwebhooks","1.guides\u002F7.webhooks",{"title":54,"path":55,"stem":56},"Idempotency","\u002Fguides\u002Fidempotency","1.guides\u002F8.idempotency",{"title":58,"path":59,"stem":60},"Testing","\u002Fguides\u002Ftesting","1.guides\u002F9.testing",false,{"title":63,"path":64,"stem":65,"children":66,"page":61},"Api Reference","\u002Fapi-reference","2.api-reference",[67,71,75,79,83,87,91,95,99,103,107,111],{"title":68,"path":69,"stem":70},"Customers","\u002Fapi-reference\u002Fcustomers","2.api-reference\u002F1.customers",{"title":72,"path":73,"stem":74},"Webhook Endpoints","\u002Fapi-reference\u002Fwebhook-endpoints","2.api-reference\u002F10.webhook-endpoints",{"title":76,"path":77,"stem":78},"Test helpers","\u002Fapi-reference\u002Ftest-helpers","2.api-reference\u002F11.test-helpers",{"title":80,"path":81,"stem":82},"Checkouts","\u002Fapi-reference\u002Fcheckouts","2.api-reference\u002F2.checkouts",{"title":84,"path":85,"stem":86},"One-off Products","\u002Fapi-reference\u002Fone-off-products","2.api-reference\u002F3.one-off-products",{"title":88,"path":89,"stem":90},"Subscriptions","\u002Fapi-reference\u002Fsubscriptions","2.api-reference\u002F4.subscriptions",{"title":92,"path":93,"stem":94},"Subscription Plans","\u002Fapi-reference\u002Fsubscription-plans","2.api-reference\u002F5.subscription-plans",{"title":96,"path":97,"stem":98},"Orders","\u002Fapi-reference\u002Forders","2.api-reference\u002F6.orders",{"title":100,"path":101,"stem":102},"Order Refunds","\u002Fapi-reference\u002Frefunds","2.api-reference\u002F7.refunds",{"title":104,"path":105,"stem":106},"Refunds","\u002Fapi-reference\u002Fglobal-refunds","2.api-reference\u002F8.global-refunds",{"title":108,"path":109,"stem":110},"Chargebacks","\u002Fapi-reference\u002Fchargebacks","2.api-reference\u002F9.chargebacks",{"title":112,"path":113,"stem":114},"Webhook Events","\u002Fapi-reference\u002Fwebhook-events","2.api-reference\u002F99.webhook-events",{"title":116,"path":117,"stem":118,"children":119},"Packages","\u002Fpackages","3.packages",[120,122,164,210,244,283,318,366,408],{"title":116,"path":117,"stem":121},"3.packages\u002Findex",{"title":123,"path":124,"stem":125,"children":126,"page":61},"PHP SDK","\u002Fpackages\u002Fphp","3.packages\u002F1.php",[127,131,134,137,140,144,147,151,154,157,160],{"title":128,"path":129,"stem":130},"Getting started","\u002Fpackages\u002Fphp\u002Freadme","3.packages\u002F1.php\u002F00.readme",{"title":68,"path":132,"stem":133},"\u002Fpackages\u002Fphp\u002Fcustomers","3.packages\u002F1.php\u002F02.customers",{"title":80,"path":135,"stem":136},"\u002Fpackages\u002Fphp\u002Fcheckouts","3.packages\u002F1.php\u002F03.checkouts",{"title":88,"path":138,"stem":139},"\u002Fpackages\u002Fphp\u002Fsubscriptions","3.packages\u002F1.php\u002F04.subscriptions",{"title":141,"path":142,"stem":143},"Subscription plans","\u002Fpackages\u002Fphp\u002Fsubscriptionplans","3.packages\u002F1.php\u002F05.subscriptionplans",{"title":96,"path":145,"stem":146},"\u002Fpackages\u002Fphp\u002Forders","3.packages\u002F1.php\u002F06.orders",{"title":148,"path":149,"stem":150},"One-off products","\u002Fpackages\u002Fphp\u002Foneoffproducts","3.packages\u002F1.php\u002F07.oneoffproducts",{"title":104,"path":152,"stem":153},"\u002Fpackages\u002Fphp\u002Frefunds","3.packages\u002F1.php\u002F08.refunds",{"title":108,"path":155,"stem":156},"\u002Fpackages\u002Fphp\u002Fchargebacks","3.packages\u002F1.php\u002F09.chargebacks",{"title":50,"path":158,"stem":159},"\u002Fpackages\u002Fphp\u002Fwebhooks","3.packages\u002F1.php\u002F10.webhooks",{"title":161,"path":162,"stem":163},"Webhook URLs","\u002Fpackages\u002Fphp\u002Fwebhookendpoints","3.packages\u002F1.php\u002F11.webhookendpoints",{"title":165,"path":166,"stem":167,"children":168,"page":61},"PHP Fluent","\u002Fpackages\u002Ffluent-php","3.packages\u002F2.fluent-php",[169,172,176,180,183,186,189,192,196,199,203,207],{"title":128,"path":170,"stem":171},"\u002Fpackages\u002Ffluent-php\u002Freadme","3.packages\u002F2.fluent-php\u002F00.readme",{"title":173,"path":174,"stem":175},"Configuration","\u002Fpackages\u002Ffluent-php\u002Fconfiguration","3.packages\u002F2.fluent-php\u002F01.configuration",{"title":177,"path":178,"stem":179},"Core concepts","\u002Fpackages\u002Ffluent-php\u002Fcoreconcepts","3.packages\u002F2.fluent-php\u002F02.coreconcepts",{"title":68,"path":181,"stem":182},"\u002Fpackages\u002Ffluent-php\u002Fcustomers","3.packages\u002F2.fluent-php\u002F03.customers",{"title":80,"path":184,"stem":185},"\u002Fpackages\u002Ffluent-php\u002Fcheckouts","3.packages\u002F2.fluent-php\u002F04.checkouts",{"title":88,"path":187,"stem":188},"\u002Fpackages\u002Ffluent-php\u002Fsubscriptions","3.packages\u002F2.fluent-php\u002F05.subscriptions",{"title":96,"path":190,"stem":191},"\u002Fpackages\u002Ffluent-php\u002Forders","3.packages\u002F2.fluent-php\u002F06.orders",{"title":193,"path":194,"stem":195},"Customer portal","\u002Fpackages\u002Ffluent-php\u002Fcustomerportal","3.packages\u002F2.fluent-php\u002F07.customerportal",{"title":50,"path":197,"stem":198},"\u002Fpackages\u002Ffluent-php\u002Fwebhooks","3.packages\u002F2.fluent-php\u002F08.webhooks",{"title":200,"path":201,"stem":202},"Webhook event flow","\u002Fpackages\u002Ffluent-php\u002Fwebhook-flow","3.packages\u002F2.fluent-php\u002F09.webhook-flow",{"title":204,"path":205,"stem":206},"Existing tables","\u002Fpackages\u002Ffluent-php\u002Fhost-owns-its-tables","3.packages\u002F2.fluent-php\u002F10.host-owns-its-tables",{"title":58,"path":208,"stem":209},"\u002Fpackages\u002Ffluent-php\u002Ftesting","3.packages\u002F2.fluent-php\u002F11.testing",{"title":211,"path":212,"stem":213,"children":214,"page":61},"Laravel","\u002Fpackages\u002Flaravel","3.packages\u002F3.laravel",[215,218,221,224,227,230,233,236,240],{"title":128,"path":216,"stem":217},"\u002Fpackages\u002Flaravel\u002Freadme","3.packages\u002F3.laravel\u002F00.readme",{"title":173,"path":219,"stem":220},"\u002Fpackages\u002Flaravel\u002Fconfiguration","3.packages\u002F3.laravel\u002F01.configuration",{"title":68,"path":222,"stem":223},"\u002Fpackages\u002Flaravel\u002Fcustomers","3.packages\u002F3.laravel\u002F02.customers",{"title":80,"path":225,"stem":226},"\u002Fpackages\u002Flaravel\u002Fcheckouts","3.packages\u002F3.laravel\u002F03.checkouts",{"title":88,"path":228,"stem":229},"\u002Fpackages\u002Flaravel\u002Fsubscriptions","3.packages\u002F3.laravel\u002F04.subscriptions",{"title":96,"path":231,"stem":232},"\u002Fpackages\u002Flaravel\u002Forders","3.packages\u002F3.laravel\u002F05.orders",{"title":50,"path":234,"stem":235},"\u002Fpackages\u002Flaravel\u002Fwebhooks","3.packages\u002F3.laravel\u002F06.webhooks",{"title":237,"path":238,"stem":239},"Comparison","\u002Fpackages\u002Flaravel\u002Fcomparison","3.packages\u002F3.laravel\u002F07.comparison",{"title":241,"path":242,"stem":243},"Migrating from Cashier","\u002Fpackages\u002Flaravel\u002Fmigrating-to-vatly","3.packages\u002F3.laravel\u002F08.migrating-to-vatly",{"title":245,"path":246,"stem":247,"children":248,"page":61},"JavaScript SDK","\u002Fpackages\u002Fjavascript","3.packages\u002F4.javascript",[249,252,255,258,261,264,267,270,273,276,279],{"title":128,"path":250,"stem":251},"\u002Fpackages\u002Fjavascript\u002Freadme","3.packages\u002F4.javascript\u002F00.readme",{"title":68,"path":253,"stem":254},"\u002Fpackages\u002Fjavascript\u002Fcustomers","3.packages\u002F4.javascript\u002F02.customers",{"title":80,"path":256,"stem":257},"\u002Fpackages\u002Fjavascript\u002Fcheckouts","3.packages\u002F4.javascript\u002F03.checkouts",{"title":88,"path":259,"stem":260},"\u002Fpackages\u002Fjavascript\u002Fsubscriptions","3.packages\u002F4.javascript\u002F04.subscriptions",{"title":141,"path":262,"stem":263},"\u002Fpackages\u002Fjavascript\u002Fsubscriptionplans","3.packages\u002F4.javascript\u002F05.subscriptionplans",{"title":96,"path":265,"stem":266},"\u002Fpackages\u002Fjavascript\u002Forders","3.packages\u002F4.javascript\u002F06.orders",{"title":148,"path":268,"stem":269},"\u002Fpackages\u002Fjavascript\u002Foneoffproducts","3.packages\u002F4.javascript\u002F07.oneoffproducts",{"title":104,"path":271,"stem":272},"\u002Fpackages\u002Fjavascript\u002Frefunds","3.packages\u002F4.javascript\u002F08.refunds",{"title":108,"path":274,"stem":275},"\u002Fpackages\u002Fjavascript\u002Fchargebacks","3.packages\u002F4.javascript\u002F09.chargebacks",{"title":50,"path":277,"stem":278},"\u002Fpackages\u002Fjavascript\u002Fwebhooks","3.packages\u002F4.javascript\u002F10.webhooks",{"title":280,"path":281,"stem":282},"API reference","\u002Fpackages\u002Fjavascript\u002Fapi","3.packages\u002F4.javascript\u002F12.api",{"title":284,"path":285,"stem":286,"children":287,"page":61},"JavaScript Fluent","\u002Fpackages\u002Ffluent-js","3.packages\u002F5.fluent-js",[288,291,294,297,300,303,306,309,312,315],{"title":128,"path":289,"stem":290},"\u002Fpackages\u002Ffluent-js\u002Freadme","3.packages\u002F5.fluent-js\u002F00.readme",{"title":173,"path":292,"stem":293},"\u002Fpackages\u002Ffluent-js\u002Fconfiguration","3.packages\u002F5.fluent-js\u002F01.configuration",{"title":177,"path":295,"stem":296},"\u002Fpackages\u002Ffluent-js\u002Fcoreconcepts","3.packages\u002F5.fluent-js\u002F02.coreconcepts",{"title":68,"path":298,"stem":299},"\u002Fpackages\u002Ffluent-js\u002Fcustomers","3.packages\u002F5.fluent-js\u002F03.customers",{"title":80,"path":301,"stem":302},"\u002Fpackages\u002Ffluent-js\u002Fcheckouts","3.packages\u002F5.fluent-js\u002F04.checkouts",{"title":88,"path":304,"stem":305},"\u002Fpackages\u002Ffluent-js\u002Fsubscriptions","3.packages\u002F5.fluent-js\u002F05.subscriptions",{"title":96,"path":307,"stem":308},"\u002Fpackages\u002Ffluent-js\u002Forders","3.packages\u002F5.fluent-js\u002F06.orders",{"title":193,"path":310,"stem":311},"\u002Fpackages\u002Ffluent-js\u002Fcustomerportal","3.packages\u002F5.fluent-js\u002F07.customerportal",{"title":50,"path":313,"stem":314},"\u002Fpackages\u002Ffluent-js\u002Fwebhooks","3.packages\u002F5.fluent-js\u002F08.webhooks",{"title":58,"path":316,"stem":317},"\u002Fpackages\u002Ffluent-js\u002Ftesting","3.packages\u002F5.fluent-js\u002F11.testing",{"title":319,"path":320,"stem":321,"children":322,"page":61},"Python SDK","\u002Fpackages\u002Fpython","3.packages\u002F6.python",[323,326,329,332,335,338,341,344,347,350,353,356,359,362],{"title":128,"path":324,"stem":325},"\u002Fpackages\u002Fpython\u002Freadme","3.packages\u002F6.python\u002F00.readme",{"title":173,"path":327,"stem":328},"\u002Fpackages\u002Fpython\u002Fconfiguration","3.packages\u002F6.python\u002F01.configuration",{"title":68,"path":330,"stem":331},"\u002Fpackages\u002Fpython\u002Fcustomers","3.packages\u002F6.python\u002F02.customers",{"title":80,"path":333,"stem":334},"\u002Fpackages\u002Fpython\u002Fcheckouts","3.packages\u002F6.python\u002F03.checkouts",{"title":88,"path":336,"stem":337},"\u002Fpackages\u002Fpython\u002Fsubscriptions","3.packages\u002F6.python\u002F04.subscriptions",{"title":141,"path":339,"stem":340},"\u002Fpackages\u002Fpython\u002Fsubscriptionplans","3.packages\u002F6.python\u002F05.subscriptionplans",{"title":96,"path":342,"stem":343},"\u002Fpackages\u002Fpython\u002Forders","3.packages\u002F6.python\u002F06.orders",{"title":148,"path":345,"stem":346},"\u002Fpackages\u002Fpython\u002Foneoffproducts","3.packages\u002F6.python\u002F07.oneoffproducts",{"title":104,"path":348,"stem":349},"\u002Fpackages\u002Fpython\u002Frefunds","3.packages\u002F6.python\u002F08.refunds",{"title":108,"path":351,"stem":352},"\u002Fpackages\u002Fpython\u002Fchargebacks","3.packages\u002F6.python\u002F09.chargebacks",{"title":50,"path":354,"stem":355},"\u002Fpackages\u002Fpython\u002Fwebhooks","3.packages\u002F6.python\u002F10.webhooks",{"title":161,"path":357,"stem":358},"\u002Fpackages\u002Fpython\u002Fwebhookendpoints","3.packages\u002F6.python\u002F11.webhookendpoints",{"title":280,"path":360,"stem":361},"\u002Fpackages\u002Fpython\u002Fapi","3.packages\u002F6.python\u002F12.api",{"title":363,"path":364,"stem":365},"Development","\u002Fpackages\u002Fpython\u002Fdevelopment","3.packages\u002F6.python\u002F13.development",{"title":367,"path":368,"stem":369,"children":370,"page":61},"Python Fluent","\u002Fpackages\u002Ffluent-python","3.packages\u002F7.fluent-python",[371,374,377,380,383,386,389,392,395,398,401,405],{"title":128,"path":372,"stem":373},"\u002Fpackages\u002Ffluent-python\u002Freadme","3.packages\u002F7.fluent-python\u002F00.readme",{"title":173,"path":375,"stem":376},"\u002Fpackages\u002Ffluent-python\u002Fconfiguration","3.packages\u002F7.fluent-python\u002F01.configuration",{"title":177,"path":378,"stem":379},"\u002Fpackages\u002Ffluent-python\u002Fcoreconcepts","3.packages\u002F7.fluent-python\u002F02.coreconcepts",{"title":68,"path":381,"stem":382},"\u002Fpackages\u002Ffluent-python\u002Fcustomers","3.packages\u002F7.fluent-python\u002F03.customers",{"title":80,"path":384,"stem":385},"\u002Fpackages\u002Ffluent-python\u002Fcheckouts","3.packages\u002F7.fluent-python\u002F04.checkouts",{"title":88,"path":387,"stem":388},"\u002Fpackages\u002Ffluent-python\u002Fsubscriptions","3.packages\u002F7.fluent-python\u002F05.subscriptions",{"title":96,"path":390,"stem":391},"\u002Fpackages\u002Ffluent-python\u002Forders","3.packages\u002F7.fluent-python\u002F06.orders",{"title":193,"path":393,"stem":394},"\u002Fpackages\u002Ffluent-python\u002Fcustomerportal","3.packages\u002F7.fluent-python\u002F07.customerportal",{"title":50,"path":396,"stem":397},"\u002Fpackages\u002Ffluent-python\u002Fwebhooks","3.packages\u002F7.fluent-python\u002F08.webhooks",{"title":58,"path":399,"stem":400},"\u002Fpackages\u002Ffluent-python\u002Ftesting","3.packages\u002F7.fluent-python\u002F11.testing",{"title":402,"path":403,"stem":404},"Porting","\u002Fpackages\u002Ffluent-python\u002Fporting","3.packages\u002F7.fluent-python\u002F12.porting",{"title":363,"path":406,"stem":407},"\u002Fpackages\u002Ffluent-python\u002Fdevelopment","3.packages\u002F7.fluent-python\u002F13.development",{"title":409,"path":410,"stem":411,"children":412,"page":61},"Django","\u002Fpackages\u002Fdjango","3.packages\u002F8.django",[413,416,420,423,427,431,434,437,440,444,447],{"title":128,"path":414,"stem":415},"\u002Fpackages\u002Fdjango\u002Freadme","3.packages\u002F8.django\u002F00.readme",{"title":417,"path":418,"stem":419},"Installation","\u002Fpackages\u002Fdjango\u002Finstallation","3.packages\u002F8.django\u002F01.installation",{"title":173,"path":421,"stem":422},"\u002Fpackages\u002Fdjango\u002Fconfiguration","3.packages\u002F8.django\u002F02.configuration",{"title":424,"path":425,"stem":426},"Billable models","\u002Fpackages\u002Fdjango\u002Fbillablemodels","3.packages\u002F8.django\u002F03.billablemodels",{"title":428,"path":429,"stem":430},"Checkout","\u002Fpackages\u002Fdjango\u002Fcheckout","3.packages\u002F8.django\u002F04.checkout",{"title":88,"path":432,"stem":433},"\u002Fpackages\u002Fdjango\u002Fbilling","3.packages\u002F8.django\u002F05.billing",{"title":193,"path":435,"stem":436},"\u002Fpackages\u002Fdjango\u002Fcustomerportal","3.packages\u002F8.django\u002F06.customerportal",{"title":50,"path":438,"stem":439},"\u002Fpackages\u002Fdjango\u002Fwebhooks","3.packages\u002F8.django\u002F07.webhooks",{"title":441,"path":442,"stem":443},"Admin","\u002Fpackages\u002Fdjango\u002Fadmin","3.packages\u002F8.django\u002F08.admin",{"title":58,"path":445,"stem":446},"\u002Fpackages\u002Fdjango\u002Ftesting","3.packages\u002F8.django\u002F09.testing",{"title":363,"path":448,"stem":449},"\u002Fpackages\u002Fdjango\u002Fdevelopment","3.packages\u002F8.django\u002F10.development",{"title":451,"path":452,"stem":453,"children":454},"Integrations","\u002Fintegrations","4.integrations",[455,457,461],{"title":451,"path":452,"stem":456},"4.integrations\u002Findex",{"title":458,"path":459,"stem":460},"Lovable payments with Vatly","\u002Fintegrations\u002Flovable","4.integrations\u002F1.lovable",{"title":462,"path":463,"stem":464},"Bolt payments with Vatly","\u002Fintegrations\u002Fbolt","4.integrations\u002F2.bolt",{"id":466,"title":50,"body":467,"description":1308,"extension":1309,"links":1310,"meta":1311,"navigation":1312,"path":197,"seo":1313,"stem":198,"__hash__":1314},"docs\u002F3.packages\u002F2.fluent-php\u002F08.webhooks.md",{"type":468,"value":469,"toc":1303},"minimark",[470,483,701,704,709,716,723,727,730,733,769,779,1256,1259,1262,1265,1269,1288,1299],[471,472,473,474,478,479,482],"p",{},"Keep local billing records current after checkout, renewals, and portal changes. Read the raw request body and the ",[475,476,477],"code",{},"Vatly-Signature"," header, then pass both unchanged to Fluent. This receiver function uses the fully wired instance from ",[480,481,177],"a",{"href":178},":",[484,485,490],"pre",{"className":486,"code":487,"language":488,"meta":489,"style":489},"language-php shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","use Vatly\\Fluent\\Exceptions\\InvalidWebhookSignatureException;\nuse Vatly\\Fluent\\Vatly;\n\nfunction receiveWebhook(Vatly $vatly, string $rawBody, string $signature): int\n{\n    try {\n        $vatly->webhookProcessor()->handle($rawBody, $signature);\n        return 201;\n    } catch (InvalidWebhookSignatureException) {\n        return 403;\n    }\n}\n","php","",[475,491,492,525,543,550,599,605,615,649,660,679,689,695],{"__ignoreMap":489},[493,494,497,501,505,509,512,514,517,519,522],"span",{"class":495,"line":496},"line",1,[493,498,500],{"class":499},"sbssI","use",[493,502,504],{"class":503},"sTEyZ"," Vatly",[493,506,508],{"class":507},"sMK4o","\\",[493,510,511],{"class":503},"Fluent",[493,513,508],{"class":507},[493,515,516],{"class":503},"Exceptions",[493,518,508],{"class":507},[493,520,521],{"class":503},"InvalidWebhookSignatureException",[493,523,524],{"class":507},";\n",[493,526,528,530,532,534,536,538,541],{"class":495,"line":527},2,[493,529,500],{"class":499},[493,531,504],{"class":503},[493,533,508],{"class":507},[493,535,511],{"class":503},[493,537,508],{"class":507},[493,539,540],{"class":503},"Vatly",[493,542,524],{"class":507},[493,544,546],{"class":495,"line":545},3,[493,547,549],{"emptyLinePlaceholder":548},true,"\n",[493,551,553,557,561,564,567,570,573,576,579,581,584,586,588,590,593,596],{"class":495,"line":552},4,[493,554,556],{"class":555},"spNyl","function",[493,558,560],{"class":559},"s2Zo4"," receiveWebhook",[493,562,563],{"class":507},"(",[493,565,540],{"class":566},"sBMFI",[493,568,569],{"class":507}," $",[493,571,572],{"class":503},"vatly",[493,574,575],{"class":507},",",[493,577,578],{"class":499}," string",[493,580,569],{"class":507},[493,582,583],{"class":503},"rawBody",[493,585,575],{"class":507},[493,587,578],{"class":499},[493,589,569],{"class":507},[493,591,592],{"class":503},"signature",[493,594,595],{"class":507},"):",[493,597,598],{"class":499}," int\n",[493,600,602],{"class":495,"line":601},5,[493,603,604],{"class":507},"{\n",[493,606,608,612],{"class":495,"line":607},6,[493,609,611],{"class":610},"s7zQu","    try",[493,613,614],{"class":507}," {\n",[493,616,618,621,623,626,629,632,635,638,640,642,644,646],{"class":495,"line":617},7,[493,619,620],{"class":507},"        $",[493,622,572],{"class":503},[493,624,625],{"class":507},"->",[493,627,628],{"class":559},"webhookProcessor",[493,630,631],{"class":507},"()->",[493,633,634],{"class":559},"handle",[493,636,637],{"class":507},"($",[493,639,583],{"class":503},[493,641,575],{"class":507},[493,643,569],{"class":507},[493,645,592],{"class":503},[493,647,648],{"class":507},");\n",[493,650,652,655,658],{"class":495,"line":651},8,[493,653,654],{"class":610},"        return",[493,656,657],{"class":499}," 201",[493,659,524],{"class":507},[493,661,663,666,669,672,674,677],{"class":495,"line":662},9,[493,664,665],{"class":507},"    }",[493,667,668],{"class":610}," catch",[493,670,671],{"class":507}," (",[493,673,521],{"class":566},[493,675,676],{"class":507},")",[493,678,614],{"class":507},[493,680,682,684,687],{"class":495,"line":681},10,[493,683,654],{"class":610},[493,685,686],{"class":499}," 403",[493,688,524],{"class":507},[493,690,692],{"class":495,"line":691},11,[493,693,694],{"class":507},"    }\n",[493,696,698],{"class":495,"line":697},12,[493,699,700],{"class":507},"}\n",[471,702,703],{},"Use the returned integer as your HTTP response status. Other failures must produce a non-2xx response so Vatly can retry. Register your public HTTPS receiver in Vatly, set its signing secret in your configuration, and exempt only that route from browser CSRF checks. Never disable signature verification.",[705,706,708],"h2",{"id":707},"verify-the-exact-bytes","Verify the exact bytes",[471,710,711,712,715],{},"The header format is ",[475,713,714],{},"t=\u003Cunix_seconds>,v1=\u003Chex_hmac_sha256>",". The signed message is the timestamp, a dot, and the raw body. The SDK checks the signature and timestamp replay window, then validates the envelope. Do not decode and re-encode JSON before verification. Use the endpoint's signing secret, not your API key.",[471,717,718,719,722],{},"Valid deliveries are recorded, mapped to typed events, passed through built-in reactions, then dispatched to your event bus. The signed resource snapshot supplies the data; built-in reactions do not make enrichment API calls. See ",[480,720,721],{"href":201},"Event flow"," for the storage mapping.",[705,724,726],{"id":725},"make-retries-transactional","Make retries transactional",[471,728,729],{},"The simple receiver above does not guarantee idempotency. Fluent's audit repository is not a processed-event ledger, and listeners can run again on redelivery. Use a durable event claim in the same database transaction as the audit log, billing writes, and outbox writes.",[471,731,732],{},"Here is a complete SQLite transaction boundary. Create this table once in your application's migrations:",[484,734,738],{"className":735,"code":736,"language":737,"meta":489,"style":489},"language-sql shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","CREATE TABLE processed_vatly_events (\n    event_id TEXT PRIMARY KEY\n);\n","sql",[475,739,740,754,765],{"__ignoreMap":489},[493,741,742,745,748,751],{"class":495,"line":496},[493,743,744],{"class":499},"CREATE",[493,746,747],{"class":499}," TABLE",[493,749,750],{"class":559}," processed_vatly_events",[493,752,753],{"class":503}," (\n",[493,755,756,759,762],{"class":495,"line":527},[493,757,758],{"class":503},"    event_id ",[493,760,761],{"class":555},"TEXT",[493,763,764],{"class":555}," PRIMARY KEY\n",[493,766,767],{"class":495,"line":545},[493,768,648],{"class":503},[471,770,771,772,775,776,482],{},"Use a PDO connection in exception mode. Every repository and your outbox dispatcher must share this connection and must not commit independently. Supply the same ",[475,773,774],{},"$secret"," that is configured on ",[475,777,778],{},"$vatly",[484,780,782],{"className":486,"code":781,"language":488,"meta":489,"style":489},"use Vatly\\API\\Exceptions\\InvalidSignatureException;\nuse Vatly\\API\\Webhooks\\Webhook;\nuse Vatly\\Fluent\\Vatly;\n\nfunction receiveOnce(PDO $db, Vatly $vatly, string $body, string $signature, string $secret): int\n{\n    try {\n        $verified = Webhook::parse($body, $signature, $secret);\n    } catch (InvalidSignatureException | InvalidArgumentException) {\n        return 403;\n    }\n\n    $db->beginTransaction();\n    try {\n        $claim = $db->prepare('INSERT OR IGNORE INTO processed_vatly_events (event_id) VALUES (?)');\n        $claim->execute([$verified->id]);\n        if ($claim->rowCount() === 0) {\n            $db->commit();\n            return 201;\n        }\n\n        $vatly->webhookProcessor()->handle($body, $signature);\n        $db->commit();\n        return 201;\n    } catch (Throwable $error) {\n        $db->rollBack();\n        throw $error;\n    }\n}\n",[475,783,784,806,827,843,847,902,906,912,949,969,977,981,985,1001,1008,1058,1085,1114,1129,1139,1145,1150,1177,1190,1199,1220,1234,1246,1251],{"__ignoreMap":489},[493,785,786,788,790,792,795,797,799,801,804],{"class":495,"line":496},[493,787,500],{"class":499},[493,789,504],{"class":503},[493,791,508],{"class":507},[493,793,794],{"class":503},"API",[493,796,508],{"class":507},[493,798,516],{"class":503},[493,800,508],{"class":507},[493,802,803],{"class":503},"InvalidSignatureException",[493,805,524],{"class":507},[493,807,808,810,812,814,816,818,820,822,825],{"class":495,"line":527},[493,809,500],{"class":499},[493,811,504],{"class":503},[493,813,508],{"class":507},[493,815,794],{"class":503},[493,817,508],{"class":507},[493,819,50],{"class":503},[493,821,508],{"class":507},[493,823,824],{"class":503},"Webhook",[493,826,524],{"class":507},[493,828,829,831,833,835,837,839,841],{"class":495,"line":545},[493,830,500],{"class":499},[493,832,504],{"class":503},[493,834,508],{"class":507},[493,836,511],{"class":503},[493,838,508],{"class":507},[493,840,540],{"class":503},[493,842,524],{"class":507},[493,844,845],{"class":495,"line":552},[493,846,549],{"emptyLinePlaceholder":548},[493,848,849,851,854,856,859,861,864,866,868,870,872,874,876,878,881,883,885,887,889,891,893,895,898,900],{"class":495,"line":601},[493,850,556],{"class":555},[493,852,853],{"class":559}," receiveOnce",[493,855,563],{"class":507},[493,857,858],{"class":566},"PDO",[493,860,569],{"class":507},[493,862,863],{"class":503},"db",[493,865,575],{"class":507},[493,867,504],{"class":566},[493,869,569],{"class":507},[493,871,572],{"class":503},[493,873,575],{"class":507},[493,875,578],{"class":499},[493,877,569],{"class":507},[493,879,880],{"class":503},"body",[493,882,575],{"class":507},[493,884,578],{"class":499},[493,886,569],{"class":507},[493,888,592],{"class":503},[493,890,575],{"class":507},[493,892,578],{"class":499},[493,894,569],{"class":507},[493,896,897],{"class":503},"secret",[493,899,595],{"class":507},[493,901,598],{"class":499},[493,903,904],{"class":495,"line":607},[493,905,604],{"class":507},[493,907,908,910],{"class":495,"line":617},[493,909,611],{"class":610},[493,911,614],{"class":507},[493,913,914,916,919,922,925,928,931,933,935,937,939,941,943,945,947],{"class":495,"line":651},[493,915,620],{"class":507},[493,917,918],{"class":503},"verified ",[493,920,921],{"class":507},"=",[493,923,924],{"class":566}," Webhook",[493,926,927],{"class":507},"::",[493,929,930],{"class":559},"parse",[493,932,637],{"class":507},[493,934,880],{"class":503},[493,936,575],{"class":507},[493,938,569],{"class":507},[493,940,592],{"class":503},[493,942,575],{"class":507},[493,944,569],{"class":507},[493,946,897],{"class":503},[493,948,648],{"class":507},[493,950,951,953,955,957,959,962,965,967],{"class":495,"line":662},[493,952,665],{"class":507},[493,954,668],{"class":610},[493,956,671],{"class":507},[493,958,803],{"class":566},[493,960,961],{"class":507}," |",[493,963,964],{"class":566}," InvalidArgumentException",[493,966,676],{"class":507},[493,968,614],{"class":507},[493,970,971,973,975],{"class":495,"line":681},[493,972,654],{"class":610},[493,974,686],{"class":499},[493,976,524],{"class":507},[493,978,979],{"class":495,"line":691},[493,980,694],{"class":507},[493,982,983],{"class":495,"line":697},[493,984,549],{"emptyLinePlaceholder":548},[493,986,988,991,993,995,998],{"class":495,"line":987},13,[493,989,990],{"class":507},"    $",[493,992,863],{"class":503},[493,994,625],{"class":507},[493,996,997],{"class":559},"beginTransaction",[493,999,1000],{"class":507},"();\n",[493,1002,1004,1006],{"class":495,"line":1003},14,[493,1005,611],{"class":610},[493,1007,614],{"class":507},[493,1009,1011,1013,1016,1018,1020,1022,1024,1027,1029,1032,1035,1038,1041,1044,1048,1051,1054,1056],{"class":495,"line":1010},15,[493,1012,620],{"class":507},[493,1014,1015],{"class":503},"claim ",[493,1017,921],{"class":507},[493,1019,569],{"class":507},[493,1021,863],{"class":503},[493,1023,625],{"class":507},[493,1025,1026],{"class":559},"prepare",[493,1028,563],{"class":507},[493,1030,1031],{"class":507},"'",[493,1033,1034],{"class":499},"INSERT",[493,1036,1037],{"class":499}," OR",[493,1039,1040],{"class":499}," IGNORE",[493,1042,1043],{"class":499}," INTO",[493,1045,1047],{"class":1046},"sfazB"," processed_vatly_events (event_id) ",[493,1049,1050],{"class":499},"VALUES",[493,1052,1053],{"class":1046}," (?)",[493,1055,1031],{"class":507},[493,1057,648],{"class":507},[493,1059,1061,1063,1066,1068,1071,1074,1077,1079,1082],{"class":495,"line":1060},16,[493,1062,620],{"class":507},[493,1064,1065],{"class":503},"claim",[493,1067,625],{"class":507},[493,1069,1070],{"class":559},"execute",[493,1072,1073],{"class":507},"([$",[493,1075,1076],{"class":503},"verified",[493,1078,625],{"class":507},[493,1080,1081],{"class":503},"id",[493,1083,1084],{"class":507},"]);\n",[493,1086,1088,1091,1094,1096,1098,1101,1104,1107,1110,1112],{"class":495,"line":1087},17,[493,1089,1090],{"class":610},"        if",[493,1092,1093],{"class":507}," ($",[493,1095,1065],{"class":503},[493,1097,625],{"class":507},[493,1099,1100],{"class":559},"rowCount",[493,1102,1103],{"class":507},"()",[493,1105,1106],{"class":507}," ===",[493,1108,1109],{"class":499}," 0",[493,1111,676],{"class":507},[493,1113,614],{"class":507},[493,1115,1117,1120,1122,1124,1127],{"class":495,"line":1116},18,[493,1118,1119],{"class":507},"            $",[493,1121,863],{"class":503},[493,1123,625],{"class":507},[493,1125,1126],{"class":559},"commit",[493,1128,1000],{"class":507},[493,1130,1132,1135,1137],{"class":495,"line":1131},19,[493,1133,1134],{"class":610},"            return",[493,1136,657],{"class":499},[493,1138,524],{"class":507},[493,1140,1142],{"class":495,"line":1141},20,[493,1143,1144],{"class":507},"        }\n",[493,1146,1148],{"class":495,"line":1147},21,[493,1149,549],{"emptyLinePlaceholder":548},[493,1151,1153,1155,1157,1159,1161,1163,1165,1167,1169,1171,1173,1175],{"class":495,"line":1152},22,[493,1154,620],{"class":507},[493,1156,572],{"class":503},[493,1158,625],{"class":507},[493,1160,628],{"class":559},[493,1162,631],{"class":507},[493,1164,634],{"class":559},[493,1166,637],{"class":507},[493,1168,880],{"class":503},[493,1170,575],{"class":507},[493,1172,569],{"class":507},[493,1174,592],{"class":503},[493,1176,648],{"class":507},[493,1178,1180,1182,1184,1186,1188],{"class":495,"line":1179},23,[493,1181,620],{"class":507},[493,1183,863],{"class":503},[493,1185,625],{"class":507},[493,1187,1126],{"class":559},[493,1189,1000],{"class":507},[493,1191,1193,1195,1197],{"class":495,"line":1192},24,[493,1194,654],{"class":610},[493,1196,657],{"class":499},[493,1198,524],{"class":507},[493,1200,1202,1204,1206,1208,1211,1213,1216,1218],{"class":495,"line":1201},25,[493,1203,665],{"class":507},[493,1205,668],{"class":610},[493,1207,671],{"class":507},[493,1209,1210],{"class":566},"Throwable",[493,1212,569],{"class":507},[493,1214,1215],{"class":503},"error",[493,1217,676],{"class":507},[493,1219,614],{"class":507},[493,1221,1223,1225,1227,1229,1232],{"class":495,"line":1222},26,[493,1224,620],{"class":507},[493,1226,863],{"class":503},[493,1228,625],{"class":507},[493,1230,1231],{"class":559},"rollBack",[493,1233,1000],{"class":507},[493,1235,1237,1240,1242,1244],{"class":495,"line":1236},27,[493,1238,1239],{"class":610},"        throw",[493,1241,569],{"class":507},[493,1243,1215],{"class":503},[493,1245,524],{"class":507},[493,1247,1249],{"class":495,"line":1248},28,[493,1250,694],{"class":507},[493,1252,1254],{"class":495,"line":1253},29,[493,1255,700],{"class":507},[471,1257,1258],{},"The second parse inside Fluent is intentional: the first verifies the event ID before deduplication; the processor still verifies every delivery it handles. Adapt the insert to your database's conflict syntax. A lock timeout or deadlock must fail the request and retry, not return success.",[471,1260,1261],{},"Queue emails, provisioning, and other external effects through an outbox committed with the billing writes. Do not send an email inside the transaction: a rollback cannot unsend it. Make outbox consumers idempotent too. Event-ID deduplication handles redelivery of one event; unique entity IDs and idempotent fulfillment rules also protect against different events for the same payment.",[471,1263,1264],{},"Deliveries can arrive out of order. Store-or-update does not prevent an older snapshot overwriting a newer one, and find-or-skip events cannot repair a missing row. Define reconciliation and stale-event handling in your driver. Persist test mode and keep test deliveries away from live access rules.",[705,1266,1268],{"id":1267},"add-application-reactions","Add application reactions",[471,1270,1271,1272,1275,1276,1279,1280,1283,1284,1287],{},"Pass ",[475,1273,1274],{},"WebhookReactionInterface"," implementations in ",[475,1277,1278],{},"Wiring(additionalWebhookReactions: [...])",". They run after built-ins and before the final event dispatch. Typed webhook events live in ",[475,1281,1282],{},"Vatly\\API\\Webhooks\\Events","; local-row-created events live in ",[475,1285,1286],{},"Vatly\\Fluent\\Events",".",[471,1289,1290,1291,1293,1294,1287],{},"Use ",[480,1292,58],{"href":208}," to test signatures, rollback, and retries. For payload fields, see the ",[480,1295,280],{"href":1296,"rel":1297},"https:\u002F\u002Fdocs.vatly.com\u002Fapi-reference",[1298],"nofollow",[1300,1301,1302],"style",{},"html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}",{"title":489,"searchDepth":527,"depth":527,"links":1304},[1305,1306,1307],{"id":707,"depth":527,"text":708},{"id":725,"depth":527,"text":726},{"id":1267,"depth":527,"text":1268},"Keep local billing records current after checkout, renewals, and portal changes.","md",null,{},{"title":50},{"title":50,"description":1308},"oeXMN8LZl5s5XtNYcPNdOTP1e_UZNEPh6gIU9ILLaV0",[1316,1318],{"title":193,"path":194,"stem":195,"description":1317,"children":-1},"Let a customer manage billing on Vatly's hosted portal instead of building those screens yourself.",{"title":200,"path":201,"stem":202,"description":1319,"children":-1},"Choose the event that matches your task.",1794594926819]