[{"data":1,"prerenderedAt":1040},["ShallowReactive",2],{"navigation_docs":3,"-guides-payments-webhooks":119,"-guides-payments-webhooks-surround":1035},[4,25],{"title":5,"icon":6,"path":7,"stem":8,"children":9,"page":6},"Getting Started",false,"/getting-started","1.getting-started",[10,15,20],{"title":11,"path":12,"stem":13,"icon":14},"Introduction","/getting-started/introduction","1.getting-started/2.introduction","i-lucide-house",{"title":16,"path":17,"stem":18,"icon":19},"Installation","/getting-started/installation","1.getting-started/3.installation","i-lucide-download",{"title":21,"path":22,"stem":23,"icon":24},"Project Structure","/getting-started/project-structure","1.getting-started/4.project-structure","i-lucide-folder-tree",{"title":26,"icon":6,"path":27,"stem":28,"children":29,"page":6},"Guides","/guides","2. guides",[30,34,49,74],{"title":31,"path":32,"stem":33,"icon":24},"Repositories","/guides/respositories","2. guides/3. respositories",{"title":35,"icon":6,"path":36,"stem":37,"children":38,"page":6},"Authentication","/guides/authentication","2. guides/4. authentication",[39,44],{"title":40,"path":41,"stem":42,"icon":43},"Authentication Setup","/guides/authentication/setup","2. guides/4. authentication/5. setup","i-lucide-lock-keyhole",{"title":45,"path":46,"stem":47,"icon":48},"JWT Configuration","/guides/authentication/jwt-configuration","2. guides/4. authentication/6. jwt-configuration","i-lucide-key",{"title":50,"icon":6,"path":51,"stem":52,"children":53,"page":6},"Charcoles Swagger","/guides/swagger","2. guides/5. swagger",[54,59,64,69],{"title":55,"path":56,"stem":57,"icon":58},"Introduction to Charcole Swagger Documentation","/guides/swagger/introduction","2. guides/5. swagger/6. introduction","i-lucide-book-open",{"title":60,"path":61,"stem":62,"icon":63},"Adding Swagger to Existing Charcole Projects","/guides/swagger/swagger-migration","2. guides/5. swagger/7. swagger-migration","i-lucide-arrow-up-circle",{"title":65,"path":66,"stem":67,"icon":68},"Swagger for Non-Charcole Projects","/guides/swagger/non-charcole-users","2. guides/5. swagger/8. non-charcole-users","i-lucide-package-plus",{"title":70,"path":71,"stem":72,"icon":73},"Swagger Examples","/guides/swagger/swagger-examples","2. guides/5. swagger/9. swagger-examples","i-lucide-code-2",{"title":75,"icon":76,"path":77,"stem":78,"children":79,"page":6},"Charcoles Payments","i-heroicons-credit-card","/guides/payments","2. guides/6. payments",[80,84,89,94,99,104,109,114],{"title":81,"path":82,"stem":83,"icon":76},"Payments","/guides/payments/introduction","2. guides/6. payments/1. introduction",{"title":85,"path":86,"stem":87,"icon":88},"Setup","/guides/payments/setup","2. guides/6. payments/2. setup","i-heroicons-wrench-screwdriver",{"title":90,"path":91,"stem":92,"icon":93},"Providers","/guides/payments/providers","2. guides/6. payments/3. providers","i-heroicons-building-library",{"title":95,"path":96,"stem":97,"icon":98},"API Endpoints","/guides/payments/endpoints","2. guides/6. payments/4. endpoints","i-heroicons-arrows-right-left",{"title":100,"path":101,"stem":102,"icon":103},"Webhooks","/guides/payments/webhooks","2. guides/6. payments/5. webhooks","i-heroicons-bolt",{"title":105,"path":106,"stem":107,"icon":108},"Environment Variables","/guides/payments/environment-variables","2. guides/6. payments/6. environment-variables","i-heroicons-key",{"title":110,"path":111,"stem":112,"icon":113},"Using Without Charcole","/guides/payments/non-charcole-users","2. guides/6. payments/7. non-charcole-users","i-heroicons-puzzle-piece",{"title":115,"path":116,"stem":117,"icon":118},"Examples","/guides/payments/payments-examples","2. guides/6. payments/8. payments-examples","i-heroicons-code-bracket",{"id":120,"title":100,"body":121,"description":1019,"extension":1020,"links":1021,"meta":1022,"navigation":1023,"path":101,"seo":1024,"stem":102,"__hash__":1034},"docs/2. guides/6. payments/5. webhooks.md",{"type":122,"value":123,"toc":1006},"minimark",[124,129,133,146,149,152,156,188,190,194,200,295,297,301,312,628,638,640,644,647,654,809,811,815,820,823,845,851,862,865,869,876,895,902,923,932,935,937,941,944,971,977,980,982,986,1002],[125,126,128],"h2",{"id":127},"why-webhooks-are-critical","Why Webhooks Are Critical",[130,131,132],"p",{},"Users close browser tabs. They lose internet. They refresh the page. The frontend never sees the final confirmation.",[130,134,135,136,140,141,145],{},"The ",[137,138,139],"code",{},"clientSecret"," flow (Stripe) is optimistic — your app sends a secret to the frontend, the frontend handles the payment, but ",[142,143,144],"strong",{},"your server doesn't know if it succeeded unless a webhook arrives",".",[130,147,148],{},"Never fulfill an order based on a frontend callback alone. Webhooks are the only reliable confirmation.",[150,151],"hr",{},[125,153,155],{"id":154},"the-raw-body-middleware-reminder","The Raw Body Middleware Reminder",[157,158,160,168,185],"callout",{"type":159},"info",[130,161,162,163,167],{},"Webhook signature verification requires the raw request body before Express parses it. If you haven't done this yet, see ",[164,165,85],"a",{"href":166},"/guides/payments/setup#the-critical-raw-body-middleware"," for details.",[130,169,170,171,174,175,178,179,178,182,145],{},"Register ",[137,172,173],{},"express.raw()"," on ",[137,176,177],{},"/payments/webhook"," ",[142,180,181],{},"before",[137,183,184],{},"app.use(express.json())",[130,186,187],{},"This is non-negotiable. Signature verification will fail silently if you skip it.",[150,189],{},[125,191,193],{"id":192},"payment-events-reference","Payment Events Reference",[130,195,196,197,199],{},"Your payment provider fires these events to ",[137,198,177],{},":",[201,202,203,219],"table",{},[204,205,206],"thead",{},[207,208,209,213,216],"tr",{},[210,211,212],"th",{},"Event",[210,214,215],{},"Provider",[210,217,218],{},"Meaning",[220,221,222,236,248,260,272,283],"tbody",{},[207,223,224,230,233],{},[225,226,227],"td",{},[137,228,229],{},"payment_intent.succeeded",[225,231,232],{},"Stripe",[225,234,235],{},"Payment confirmed — fulfill order",[207,237,238,243,245],{},[225,239,240],{},[137,241,242],{},"payment_intent.payment_failed",[225,244,232],{},[225,246,247],{},"Payment failed or was cancelled",[207,249,250,255,257],{},[225,251,252],{},[137,253,254],{},"charge.refunded",[225,256,232],{},[225,258,259],{},"Refund was processed",[207,261,262,267,270],{},[225,263,264],{},[137,265,266],{},"order_created",[225,268,269],{},"LemonSqueezy",[225,271,235],{},[207,273,274,279,281],{},[225,275,276],{},[137,277,278],{},"order_refunded",[225,280,269],{},[225,282,259],{},[207,284,285,290,292],{},[225,286,287],{},[137,288,289],{},"subscription_cancelled",[225,291,269],{},[225,293,294],{},"Subscription ended",[150,296],{},[125,298,300],{"id":299},"add-your-fulfillment-logic","Add Your Fulfillment Logic",[130,302,303,304,307,308,311],{},"The generated ",[137,305,306],{},"payments.controller.js"," includes a ",[137,309,310],{},"switch"," statement with placeholders. This is where you add your fulfillment logic:",[313,314,319],"pre",{"className":315,"code":316,"language":317,"meta":318,"style":318},"language-js shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","import { PAYMENT_EVENTS } from '@charcoles/payments'\n\n// In your webhook handler\nswitch (result.event) {\n  case PAYMENT_EVENTS.STRIPE_PAYMENT_SUCCEEDED:\n  case PAYMENT_EVENTS.LS_ORDER_CREATED:\n    // Add your logic here\n    await sendConfirmationEmail(result.data)\n    await updateOrderStatus(result.data.id, 'paid')\n    await grantProductAccess(result.data)\n    break\n\n  case PAYMENT_EVENTS.STRIPE_PAYMENT_FAILED:\n    // Handle payment failures\n    await notifyCustomerOfFailure(result.data)\n    break\n\n  case PAYMENT_EVENTS.LS_ORDER_REFUNDED:\n  case PAYMENT_EVENTS.STRIPE_REFUND_CREATED:\n    // Handle refunds\n    await updateOrderStatus(result.data.id, 'refunded')\n    await revokeProductAccess(result.data)\n    break\n}\n","js","",[137,320,321,354,361,368,384,400,414,420,438,472,486,492,497,511,517,531,536,541,555,569,575,603,617,622],{"__ignoreMap":318},[322,323,326,330,334,338,341,344,347,351],"span",{"class":324,"line":325},"line",1,[322,327,329],{"class":328},"s7zQu","import",[322,331,333],{"class":332},"sMK4o"," {",[322,335,337],{"class":336},"sTEyZ"," PAYMENT_EVENTS",[322,339,340],{"class":332}," }",[322,342,343],{"class":328}," from",[322,345,346],{"class":332}," '",[322,348,350],{"class":349},"sfazB","@charcoles/payments",[322,352,353],{"class":332},"'\n",[322,355,357],{"class":324,"line":356},2,[322,358,360],{"emptyLinePlaceholder":359},true,"\n",[322,362,364],{"class":324,"line":363},3,[322,365,367],{"class":366},"sHwdD","// In your webhook handler\n",[322,369,371,373,376,378,381],{"class":324,"line":370},4,[322,372,310],{"class":328},[322,374,375],{"class":336}," (result",[322,377,145],{"class":332},[322,379,380],{"class":336},"event) ",[322,382,383],{"class":332},"{\n",[322,385,387,390,392,394,397],{"class":324,"line":386},5,[322,388,389],{"class":328},"  case",[322,391,337],{"class":336},[322,393,145],{"class":332},[322,395,396],{"class":336},"STRIPE_PAYMENT_SUCCEEDED",[322,398,399],{"class":332},":\n",[322,401,403,405,407,409,412],{"class":324,"line":402},6,[322,404,389],{"class":328},[322,406,337],{"class":336},[322,408,145],{"class":332},[322,410,411],{"class":336},"LS_ORDER_CREATED",[322,413,399],{"class":332},[322,415,417],{"class":324,"line":416},7,[322,418,419],{"class":366},"    // Add your logic here\n",[322,421,423,426,430,433,435],{"class":324,"line":422},8,[322,424,425],{"class":328},"    await",[322,427,429],{"class":428},"s2Zo4"," sendConfirmationEmail",[322,431,432],{"class":336},"(result",[322,434,145],{"class":332},[322,436,437],{"class":336},"data)\n",[322,439,441,443,446,448,450,453,455,458,461,463,466,469],{"class":324,"line":440},9,[322,442,425],{"class":328},[322,444,445],{"class":428}," updateOrderStatus",[322,447,432],{"class":336},[322,449,145],{"class":332},[322,451,452],{"class":336},"data",[322,454,145],{"class":332},[322,456,457],{"class":336},"id",[322,459,460],{"class":332},",",[322,462,346],{"class":332},[322,464,465],{"class":349},"paid",[322,467,468],{"class":332},"'",[322,470,471],{"class":336},")\n",[322,473,475,477,480,482,484],{"class":324,"line":474},10,[322,476,425],{"class":328},[322,478,479],{"class":428}," grantProductAccess",[322,481,432],{"class":336},[322,483,145],{"class":332},[322,485,437],{"class":336},[322,487,489],{"class":324,"line":488},11,[322,490,491],{"class":328},"    break\n",[322,493,495],{"class":324,"line":494},12,[322,496,360],{"emptyLinePlaceholder":359},[322,498,500,502,504,506,509],{"class":324,"line":499},13,[322,501,389],{"class":328},[322,503,337],{"class":336},[322,505,145],{"class":332},[322,507,508],{"class":336},"STRIPE_PAYMENT_FAILED",[322,510,399],{"class":332},[322,512,514],{"class":324,"line":513},14,[322,515,516],{"class":366},"    // Handle payment failures\n",[322,518,520,522,525,527,529],{"class":324,"line":519},15,[322,521,425],{"class":328},[322,523,524],{"class":428}," notifyCustomerOfFailure",[322,526,432],{"class":336},[322,528,145],{"class":332},[322,530,437],{"class":336},[322,532,534],{"class":324,"line":533},16,[322,535,491],{"class":328},[322,537,539],{"class":324,"line":538},17,[322,540,360],{"emptyLinePlaceholder":359},[322,542,544,546,548,550,553],{"class":324,"line":543},18,[322,545,389],{"class":328},[322,547,337],{"class":336},[322,549,145],{"class":332},[322,551,552],{"class":336},"LS_ORDER_REFUNDED",[322,554,399],{"class":332},[322,556,558,560,562,564,567],{"class":324,"line":557},19,[322,559,389],{"class":328},[322,561,337],{"class":336},[322,563,145],{"class":332},[322,565,566],{"class":336},"STRIPE_REFUND_CREATED",[322,568,399],{"class":332},[322,570,572],{"class":324,"line":571},20,[322,573,574],{"class":366},"    // Handle refunds\n",[322,576,578,580,582,584,586,588,590,592,594,596,599,601],{"class":324,"line":577},21,[322,579,425],{"class":328},[322,581,445],{"class":428},[322,583,432],{"class":336},[322,585,145],{"class":332},[322,587,452],{"class":336},[322,589,145],{"class":332},[322,591,457],{"class":336},[322,593,460],{"class":332},[322,595,346],{"class":332},[322,597,598],{"class":349},"refunded",[322,600,468],{"class":332},[322,602,471],{"class":336},[322,604,606,608,611,613,615],{"class":324,"line":605},22,[322,607,425],{"class":328},[322,609,610],{"class":428}," revokeProductAccess",[322,612,432],{"class":336},[322,614,145],{"class":332},[322,616,437],{"class":336},[322,618,620],{"class":324,"line":619},23,[322,621,491],{"class":328},[322,623,625],{"class":324,"line":624},24,[322,626,627],{"class":332},"}\n",[130,629,630,631,634,635,637],{},"Your logic runs after the webhook is verified and deduplicated. If fulfillment succeeds, return ",[137,632,633],{},"200 OK",". If it fails, log the error and still return ",[137,636,633],{}," — the module will not retry you.",[150,639],{},[125,641,643],{"id":642},"webhook-deduplication","Webhook Deduplication",[130,645,646],{},"Payment providers retry webhooks when your server doesn't respond quickly or returns an error. The same event can arrive multiple times.",[130,648,649,650,653],{},"The module includes ",[142,651,652],{},"in-memory deduplication",": if the same event ID arrives within the deduplication window, it's marked as a duplicate and processing is skipped.",[157,655,657,660,806],{"type":656},"warning",[130,658,659],{},"In-memory deduplication resets when your server restarts. For production, use a persistent store (Redis, database) to track processed event IDs:",[313,661,663],{"className":315,"code":662,"language":317,"meta":318,"style":318},"// Example with Redis\nconst isProcessed = await redis.get(`event:${eventId}`)\nif (isProcessed) return { received: true, duplicate: true }\n\n// Process the event...\n\nawait redis.setex(`event:${eventId}`, 86400, 'processed') // 24 hour TTL\n",[137,664,665,670,713,748,752,757,761],{"__ignoreMap":318},[322,666,667],{"class":324,"line":325},[322,668,669],{"class":366},"// Example with Redis\n",[322,671,672,676,679,682,685,688,690,693,696,699,702,705,708,711],{"class":324,"line":356},[322,673,675],{"class":674},"spNyl","const",[322,677,678],{"class":336}," isProcessed ",[322,680,681],{"class":332},"=",[322,683,684],{"class":328}," await",[322,686,687],{"class":336}," redis",[322,689,145],{"class":332},[322,691,692],{"class":428},"get",[322,694,695],{"class":336},"(",[322,697,698],{"class":332},"`",[322,700,701],{"class":349},"event:",[322,703,704],{"class":332},"${",[322,706,707],{"class":336},"eventId",[322,709,710],{"class":332},"}`",[322,712,471],{"class":336},[322,714,715,718,721,724,726,730,732,736,738,741,743,745],{"class":324,"line":363},[322,716,717],{"class":328},"if",[322,719,720],{"class":336}," (isProcessed) ",[322,722,723],{"class":328},"return",[322,725,333],{"class":332},[322,727,729],{"class":728},"swJcz"," received",[322,731,199],{"class":332},[322,733,735],{"class":734},"sfNiH"," true",[322,737,460],{"class":332},[322,739,740],{"class":728}," duplicate",[322,742,199],{"class":332},[322,744,735],{"class":734},[322,746,747],{"class":332}," }\n",[322,749,750],{"class":324,"line":370},[322,751,360],{"emptyLinePlaceholder":359},[322,753,754],{"class":324,"line":386},[322,755,756],{"class":366},"// Process the event...\n",[322,758,759],{"class":324,"line":402},[322,760,360],{"emptyLinePlaceholder":359},[322,762,763,766,768,770,773,775,777,779,781,783,785,787,791,793,795,798,800,803],{"class":324,"line":416},[322,764,765],{"class":328},"await",[322,767,687],{"class":336},[322,769,145],{"class":332},[322,771,772],{"class":428},"setex",[322,774,695],{"class":336},[322,776,698],{"class":332},[322,778,701],{"class":349},[322,780,704],{"class":332},[322,782,707],{"class":336},[322,784,710],{"class":332},[322,786,460],{"class":332},[322,788,790],{"class":789},"sbssI"," 86400",[322,792,460],{"class":332},[322,794,346],{"class":332},[322,796,797],{"class":349},"processed",[322,799,468],{"class":332},[322,801,802],{"class":336},") ",[322,804,805],{"class":366},"// 24 hour TTL\n",[130,807,808],{},"This ensures idempotency even across server restarts.",[150,810],{},[125,812,814],{"id":813},"test-webhooks-locally","Test Webhooks Locally",[816,817,819],"h3",{"id":818},"stripe-webhook-testing","Stripe Webhook Testing",[130,821,822],{},"Use the Stripe CLI to forward webhooks to your local server:",[313,824,828],{"className":825,"code":826,"language":827,"meta":318,"style":318},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","stripe listen --forward-to localhost:3000/payments/webhook\n","bash",[137,829,830],{"__ignoreMap":318},[322,831,832,836,839,842],{"class":324,"line":325},[322,833,835],{"class":834},"sBMFI","stripe",[322,837,838],{"class":349}," listen",[322,840,841],{"class":349}," --forward-to",[322,843,844],{"class":349}," localhost:3000/payments/webhook\n",[130,846,847,848,199],{},"This creates a tunnel and prints your webhook signing secret. Add it to ",[137,849,850],{},".env.local",[313,852,856],{"className":853,"code":854,"language":855,"meta":318,"style":318},"language-env shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","STRIPE_WEBHOOK_SECRET=whsec_...\n","env",[137,857,858],{"__ignoreMap":318},[322,859,860],{"class":324,"line":325},[322,861,854],{},[130,863,864],{},"Now trigger test events in the Stripe dashboard, and they'll be forwarded to your local server.",[816,866,868],{"id":867},"lemonsqueezy-webhook-testing","LemonSqueezy Webhook Testing",[130,870,871,872,875],{},"LemonSqueezy doesn't have a built-in CLI tunnel. Use ",[137,873,874],{},"ngrok"," to expose your local server:",[313,877,879],{"className":825,"code":878,"language":827,"meta":318,"style":318},"npx ngrok http 3000\n",[137,880,881],{"__ignoreMap":318},[322,882,883,886,889,892],{"class":324,"line":325},[322,884,885],{"class":834},"npx",[322,887,888],{"class":349}," ngrok",[322,890,891],{"class":349}," http",[322,893,894],{"class":789}," 3000\n",[130,896,897,898,901],{},"This creates a public URL like ",[137,899,900],{},"https://abc123.ngrok.io",". Then:",[903,904,905,909,915,918],"ol",{},[906,907,908],"li",{},"Go to your LemonSqueezy store settings",[906,910,911,912],{},"Add a webhook endpoint: ",[137,913,914],{},"https://abc123.ngrok.io/payments/webhook",[906,916,917],{},"Copy the signing secret",[906,919,920,921,199],{},"Add to ",[137,922,850],{},[313,924,926],{"className":853,"code":925,"language":855,"meta":318,"style":318},"LEMONSQUEEZY_WEBHOOK_SECRET=...\n",[137,927,928],{"__ignoreMap":318},[322,929,930],{"class":324,"line":325},[322,931,925],{},[130,933,934],{},"Now trigger test events from the LemonSqueezy dashboard.",[150,936],{},[125,938,940],{"id":939},"webhook-security","Webhook Security",[130,942,943],{},"All webhooks are verified using cryptographic signatures:",[945,946,947,960],"ul",{},[906,948,949,952,953,956,957],{},[142,950,951],{},"Stripe:"," Uses the ",[137,954,955],{},"Stripe-Signature"," header and ",[137,958,959],{},"STRIPE_WEBHOOK_SECRET",[906,961,962,952,965,956,968],{},[142,963,964],{},"LemonSqueezy:",[137,966,967],{},"X-Signature",[137,969,970],{},"LEMONSQUEEZY_WEBHOOK_SECRET",[130,972,973,974,145],{},"If a webhook arrives without a valid signature, the module rejects it and returns ",[137,975,976],{},"401 Unauthorized",[130,978,979],{},"Never trust the webhook payload without signature verification. Always use the module's built-in verification.",[150,981],{},[125,983,985],{"id":984},"what-comes-next","What Comes Next",[945,987,988,995],{},[906,989,990,994],{},[142,991,992],{},[164,993,115],{"href":116}," — Full webhook handling examples",[906,996,997,1001],{},[142,998,999],{},[164,1000,105],{"href":106}," — Reference all webhook secrets",[1003,1004,1005],"style",{},"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 pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}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 .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}",{"title":318,"searchDepth":356,"depth":356,"links":1007},[1008,1009,1010,1011,1012,1013,1017,1018],{"id":127,"depth":356,"text":128},{"id":154,"depth":356,"text":155},{"id":192,"depth":356,"text":193},{"id":299,"depth":356,"text":300},{"id":642,"depth":356,"text":643},{"id":813,"depth":356,"text":814,"children":1014},[1015,1016],{"id":818,"depth":363,"text":819},{"id":867,"depth":363,"text":868},{"id":939,"depth":356,"text":940},{"id":984,"depth":356,"text":985},"Handle payment events reliably with webhook processing, deduplication, and testing.","md",null,{},{"icon":103},{"title":1025,"description":1026,"keywords":1027},"Payment Webhooks","Configure and handle payment webhooks from Stripe and LemonSqueezy.",[1028,1029,1030,1031,1032,1033],"webhooks","webhook events","payment events","webhook testing","stripe webhooks","lemonsqueezy webhooks","Dazh5C_gcYyeZYLwwSiNSPXdUg9tz64CGlsUPPMiuIQ",[1036,1038],{"title":95,"path":96,"stem":97,"description":1037,"icon":98,"children":-1},"Complete reference for all 4 payment endpoints with request and response examples.",{"title":105,"path":106,"stem":107,"description":1039,"icon":108,"children":-1},"Complete reference of all payment-related environment variables.",1777986766293]