payweave 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +272 -0
  3. package/dist/_tsup-dts-rollup.d.ts +19470 -0
  4. package/dist/chunk-4R5OGOCC.js +94 -0
  5. package/dist/chunk-4R5OGOCC.js.map +1 -0
  6. package/dist/chunk-535Q3GUN.js +175 -0
  7. package/dist/chunk-535Q3GUN.js.map +1 -0
  8. package/dist/chunk-67NRYGRC.js +192 -0
  9. package/dist/chunk-67NRYGRC.js.map +1 -0
  10. package/dist/chunk-CGQ73I4I.js +568 -0
  11. package/dist/chunk-CGQ73I4I.js.map +1 -0
  12. package/dist/chunk-CJEYDHY6.js +18 -0
  13. package/dist/chunk-CJEYDHY6.js.map +1 -0
  14. package/dist/chunk-CR63JAAS.js +3 -0
  15. package/dist/chunk-CR63JAAS.js.map +1 -0
  16. package/dist/chunk-E633MKVX.js +1507 -0
  17. package/dist/chunk-E633MKVX.js.map +1 -0
  18. package/dist/chunk-FBPOCXRZ.js +1135 -0
  19. package/dist/chunk-FBPOCXRZ.js.map +1 -0
  20. package/dist/chunk-HTRGMZFH.js +267 -0
  21. package/dist/chunk-HTRGMZFH.js.map +1 -0
  22. package/dist/chunk-IUVGM3K7.js +765 -0
  23. package/dist/chunk-IUVGM3K7.js.map +1 -0
  24. package/dist/chunk-JD4OZHRI.js +326 -0
  25. package/dist/chunk-JD4OZHRI.js.map +1 -0
  26. package/dist/chunk-NV3MZF7L.js +139 -0
  27. package/dist/chunk-NV3MZF7L.js.map +1 -0
  28. package/dist/chunk-NXQBP35U.js +13 -0
  29. package/dist/chunk-NXQBP35U.js.map +1 -0
  30. package/dist/chunk-QJHXDZYC.js +412 -0
  31. package/dist/chunk-QJHXDZYC.js.map +1 -0
  32. package/dist/chunk-TWZK62TZ.js +83 -0
  33. package/dist/chunk-TWZK62TZ.js.map +1 -0
  34. package/dist/cli/index.js +35381 -0
  35. package/dist/core/index.d.ts +85 -0
  36. package/dist/core/index.js +6 -0
  37. package/dist/core/index.js.map +1 -0
  38. package/dist/db/drizzle/index.d.ts +6 -0
  39. package/dist/db/drizzle/index.js +1532 -0
  40. package/dist/db/drizzle/index.js.map +1 -0
  41. package/dist/db/index.d.ts +43 -0
  42. package/dist/db/index.js +3 -0
  43. package/dist/db/index.js.map +1 -0
  44. package/dist/db/mongodb/index.d.ts +3 -0
  45. package/dist/db/mongodb/index.js +934 -0
  46. package/dist/db/mongodb/index.js.map +1 -0
  47. package/dist/db/mysql/index.d.ts +1 -0
  48. package/dist/db/mysql/index.js +12 -0
  49. package/dist/db/mysql/index.js.map +1 -0
  50. package/dist/db/postgres/index.d.ts +5 -0
  51. package/dist/db/postgres/index.js +791 -0
  52. package/dist/db/postgres/index.js.map +1 -0
  53. package/dist/db/prisma/index.d.ts +1 -0
  54. package/dist/db/prisma/index.js +12 -0
  55. package/dist/db/prisma/index.js.map +1 -0
  56. package/dist/db/sqlite/index.d.ts +5 -0
  57. package/dist/db/sqlite/index.js +750 -0
  58. package/dist/db/sqlite/index.js.map +1 -0
  59. package/dist/express/index.d.ts +1 -0
  60. package/dist/express/index.js +3 -0
  61. package/dist/express/index.js.map +1 -0
  62. package/dist/fastify/index.d.ts +1 -0
  63. package/dist/fastify/index.js +3 -0
  64. package/dist/fastify/index.js.map +1 -0
  65. package/dist/flutterwave/index.d.ts +27 -0
  66. package/dist/flutterwave/index.js +9 -0
  67. package/dist/flutterwave/index.js.map +1 -0
  68. package/dist/index.d.ts +85 -0
  69. package/dist/index.js +3677 -0
  70. package/dist/index.js.map +1 -0
  71. package/dist/next/index.d.ts +1 -0
  72. package/dist/next/index.js +3 -0
  73. package/dist/next/index.js.map +1 -0
  74. package/dist/paystack/index.d.ts +33 -0
  75. package/dist/paystack/index.js +9 -0
  76. package/dist/paystack/index.js.map +1 -0
  77. package/dist/products/index.d.ts +22 -0
  78. package/dist/products/index.js +7 -0
  79. package/dist/products/index.js.map +1 -0
  80. package/dist/testing/index.d.ts +11 -0
  81. package/dist/testing/index.js +69 -0
  82. package/dist/testing/index.js.map +1 -0
  83. package/dist/unified/index.d.ts +41 -0
  84. package/dist/unified/index.js +6 -0
  85. package/dist/unified/index.js.map +1 -0
  86. package/dist/webhooks/index.d.ts +8 -0
  87. package/dist/webhooks/index.js +8 -0
  88. package/dist/webhooks/index.js.map +1 -0
  89. package/package.json +164 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/products/subscribe.ts","../src/products/apply.ts","../src/webhooks/stripe.ts","../src/webhooks/index.ts"],"names":["created","period","meta","patch","updated","asRecord"],"mappings":";;;;;;AAyFO,IAAM,yBAAA,GAA4B,CAAC,QAAA,EAAU,UAAU;AAK9D,SAAS,yBAAyB,QAAA,EAAmE;AACnG,EAAA,OAAQ,yBAAA,CAAgD,SAAS,QAAQ,CAAA;AAC3E;AAkDO,SAAS,iBAAiB,IAAA,EAA+B;AAC9D,EAAA,OAAO,IAAA,CAAK,SAAS,IAAA,CAAK,EAAA;AAC5B;AAeA,SAAS,aAAA,CAAc,UAAwC,GAAA,EAAuC;AACpG,EAAA,MAAM,OAAA,GAAU,IAAI,OAAA,EAAQ;AAC5B,EAAA,OAAO,EAAE,KAAA,EAAO,IAAI,IAAA,CAAK,OAAO,CAAA,EAAG,GAAA,EAAK,IAAI,IAAA,CAAK,QAAQ,OAAA,EAAS,QAAA,IAAY,OAAA,EAAS,CAAC,CAAC,CAAA,EAAE;AAC7F;AAEA,SAAS,QAAA,CACP,UACA,MAAA,EAC6B;AAC7B,EAAA,OAAO,UAAU,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,OAAO,MAAM,CAAA;AAC9C;AAEA,SAAS,sBAAsB,KAAA,EAAwC;AAErE,EAAA,OAAO,IAAI,uBAAA,CAAwB,CAAA,8CAAA,EAAiD,KAAK,CAAA,CAAA,CAAG,CAAA;AAC9F;AAQA,eAAe,sBAAA,CACb,GAAA,EACA,QAAA,EACA,QAAA,EACA,aACA,UAAA,EACiB;AACjB,EAAA,MAAM,QAAA,GAAW,WAAA,CAAY,WAAA,CAAY,QAAQ,CAAA;AACjD,EAAA,IAAI,QAAA,KAAa,QAAW,OAAO,QAAA;AAEnC,EAAA,IAAI,aAAa,QAAA,EAAU;AAGzB,IAAA,IAAI,CAAC,GAAA,CAAI,MAAA,EAAQ,MAAM,IAAI,oBAAoB,0CAA0C,CAAA;AACzF,IAAA,MAAMA,QAAAA,GAAU,MAAM,GAAA,CAAI,MAAA,CAAO,UAAU,MAAA,CAAO;AAAA,MAChD,GAAI,YAAY,KAAA,KAAU,IAAA,GAAO,EAAE,KAAA,EAAO,WAAA,CAAY,KAAA,EAAM,GAAI,EAAC;AAAA,MACjE,QAAA,EAAU,EAAE,aAAA,EAAe,WAAA,CAAY,EAAA;AAAG,KAC3C,CAAA;AACD,IAAA,MAAM,SAAS,SAAA,CAAU,eAAA,CAAgB,UAAA,EAAY,QAAA,EAAUA,SAAQ,EAAE,CAAA;AACzE,IAAA,OAAOA,QAAAA,CAAQ,EAAA;AAAA,EACjB;AAGA,EAAA,IAAI,CAAC,GAAA,CAAI,QAAA,EAAU,MAAM,IAAI,oBAAoB,4CAA4C,CAAA;AAC7F,EAAA,IAAI,WAAA,CAAY,UAAU,IAAA,EAAM;AAC9B,IAAA,MAAM,IAAI,uBAAA;AAAA,MACR;AAAA,KAIF;AAAA,EACF;AACA,EAAA,MAAM,OAAA,GAAU,MAAM,GAAA,CAAI,QAAA,CAAS,UAAU,MAAA,CAAO;AAAA,IAClD,OAAO,WAAA,CAAY,KAAA;AAAA,IACnB,QAAA,EAAU,EAAE,aAAA,EAAe,WAAA,CAAY,EAAA;AAAG,GAC3C,CAAA;AACD,EAAA,MAAM,YAAA,GAAe,QAAQ,IAAA,CAAK,aAAA;AAClC,EAAA,IAAI,iBAAiB,MAAA,EAAW;AAC9B,IAAA,MAAM,IAAI,sBAAsB,qDAAA,EAAuD;AAAA,MACrF,QAAA,EAAU;AAAA,KACX,CAAA;AAAA,EACH;AACA,EAAA,MAAM,QAAA,CAAS,SAAA,CAAU,eAAA,CAAgB,UAAA,EAAY,YAAY,YAAY,CAAA;AAC7E,EAAA,OAAO,YAAA;AACT;AAGA,eAAe,qBACb,GAAA,EACA,QAAA,EACA,MACA,YAAA,EACA,WAAA,EACA,eACA,KAAA,EAC0B;AAC1B,EAAA,MAAM,UAAU,YAAA,CAAa,OAAA;AAC7B,EAAA,IAAI,YAAY,MAAA,EAAW;AACzB,IAAA,MAAM,IAAI,uBAAA;AAAA,MACR,CAAA,MAAA,EAAS,KAAK,EAAE,CAAA,0DAAA;AAAA,KAClB;AAAA,EACF;AAEA,EAAA,IAAI,CAAC,GAAA,CAAI,MAAA,EAAQ,MAAM,IAAI,oBAAoB,0CAA0C,CAAA;AAEzF,EAAA,MAAM,qBAAqB,MAAM,sBAAA;AAAA,IAC/B,GAAA;AAAA,IACA,QAAA;AAAA,IACA,QAAA;AAAA,IACA,WAAA;AAAA,IACA,KAAA,CAAM;AAAA,GACR;AAEA,EAAA,MAAM,UAAU,MAAM,GAAA,CAAI,MAAA,CAAO,QAAA,CAAS,SAAS,MAAA,CAAO;AAAA,IACxD,IAAA,EAAM,cAAA;AAAA,IACN,QAAA,EAAU,kBAAA;AAAA,IACV,YAAY,CAAC,EAAE,OAAO,OAAA,EAAS,QAAA,EAAU,GAAG,CAAA;AAAA;AAAA;AAAA;AAAA,IAI5C,qBAAqB,aAAA,CAAc,EAAA;AAAA,IACnC,QAAA,EAAU,EAAE,aAAA,EAAe,aAAA,CAAc,EAAA,EAAI,UAAU,IAAA,CAAK,EAAA,EAAI,YAAA,EAAc,WAAA,CAAY,EAAA,EAAG;AAAA,IAC7F,GAAI,MAAM,UAAA,KAAe,MAAA,GAAY,EAAE,WAAA,EAAa,KAAA,CAAM,UAAA,EAAW,GAAI,EAAC;AAAA,IAC1E,GAAI,MAAM,SAAA,KAAc,MAAA,GAAY,EAAE,UAAA,EAAY,KAAA,CAAM,SAAA,EAAU,GAAI;AAAC,GACxE,CAAA;AAED,EAAA,IAAI,CAAC,QAAQ,GAAA,EAAK;AAChB,IAAA,MAAM,IAAI,sBAAsB,wDAAA,EAA0D;AAAA,MACxF,QAAA,EAAU,QAAA;AAAA,MACV,GAAA,EAAK;AAAA,KACN,CAAA;AAAA,EACH;AACA,EAAA,OAAO,EAAE,QAAQ,UAAA,EAAY,WAAA,EAAa,QAAQ,GAAA,EAAK,SAAA,EAAW,QAAQ,EAAA,EAAG;AAC/E;AAGA,eAAe,uBACb,GAAA,EACA,QAAA,EACA,MACA,YAAA,EACA,WAAA,EACA,eACA,KAAA,EAC0B;AAC1B,EAAA,MAAM,WAAW,YAAA,CAAa,QAAA;AAC9B,EAAA,IAAI,aAAa,MAAA,EAAW;AAC1B,IAAA,MAAM,IAAI,uBAAA;AAAA,MACR,CAAA,MAAA,EAAS,KAAK,EAAE,CAAA,gEAAA;AAAA,KAClB;AAAA,EACF;AAEA,EAAA,IAAI,CAAC,GAAA,CAAI,QAAA,EAAU,MAAM,IAAI,oBAAoB,4CAA4C,CAAA;AAE7F,EAAA,MAAM,uBAAuB,GAAA,EAAK,QAAA,EAAU,UAAA,EAAY,WAAA,EAAa,MAAM,UAAU,CAAA;AAKrF,EAAA,IAAI,WAAA,CAAY,UAAU,IAAA,EAAM;AAC9B,IAAA,MAAM,IAAI,uBAAA;AAAA,MACR;AAAA,KAIF;AAAA,EACF;AAIA,EAAA,MAAM,WAAA,GAAc,KAAK,KAAA,EAAO,MAAA;AAEhC,EAAA,IAAI,gBAAgB,MAAA,EAAW;AAC7B,IAAA,MAAM,IAAI,uBAAA,CAAwB,CAAA,MAAA,EAAS,IAAA,CAAK,EAAE,CAAA,yBAAA,CAA2B,CAAA;AAAA,EAC/E;AAEA,EAAA,MAAM,GAAA,GAAM,MAAM,GAAA,CAAI,QAAA,CAAS,aAAa,UAAA,CAAW;AAAA,IACrD,OAAO,WAAA,CAAY,KAAA;AAAA,IACnB,MAAA,EAAQ,WAAA;AAAA,IACR,IAAA,EAAM,QAAA;AAAA;AAAA;AAAA;AAAA,IAIN,WAAW,aAAA,CAAc,EAAA;AAAA,IACzB,QAAA,EAAU,EAAE,aAAA,EAAe,aAAA,CAAc,EAAA,EAAI,UAAU,IAAA,CAAK,EAAA,EAAI,YAAA,EAAc,WAAA,CAAY,EAAA;AAAG,GAC9F,CAAA;AAED,EAAA,OAAO,EAAE,MAAA,EAAQ,UAAA,EAAY,WAAA,EAAa,GAAA,CAAI,KAAK,iBAAA,EAAmB,SAAA,EAAW,GAAA,CAAI,IAAA,CAAK,SAAA,EAAU;AACtG;AAMA,eAAsB,SAAA,CAAU,KAAqB,KAAA,EAAiD;AAOpG,EAAA,MAAM,WAAW,GAAA,CAAI,QAAA;AACrB,EAAA,IAAI,CAAC,QAAA,EAAU;AACb,IAAA,MAAM,IAAI,mBAAA;AAAA,MACR;AAAA,KACF;AAAA,EACF;AAEA,EAAA,MAAM,IAAA,GAAO,QAAA,CAAS,GAAA,CAAI,QAAA,EAAU,MAAM,MAAM,CAAA;AAChD,EAAA,IAAI,CAAC,IAAA,EAAM;AACT,IAAA,MAAM,IAAI,uBAAA;AAAA,MACR,CAAA,cAAA,EAAiB,MAAM,MAAM,CAAA,gEAAA;AAAA,KAC/B;AAAA,EACF;AAEA,EAAA,MAAM,QAAA,GAAW,KAAA,CAAM,QAAA,IAAY,GAAA,CAAI,eAAA;AACvC,EAAA,IAAI,CAAC,GAAA,CAAI,SAAA,CAAU,QAAA,CAAS,QAAQ,CAAA,EAAG;AACrC,IAAA,MAAM,IAAI,mBAAA;AAAA,MACR,aAAa,QAAQ,CAAA,sDAAA,EAAoD,IAAI,SAAA,CAAU,IAAA,CAAK,IAAI,CAAC,CAAA,CAAA;AAAA,KACnG;AAAA,EACF;AACA,EAAA,IAAI,CAAC,wBAAA,CAAyB,QAAQ,CAAA,EAAG;AACvC,IAAA,MAAM,IAAI,mBAAA;AAAA,MACR,aAAa,QAAQ,CAAA,yEAAA,EACW,yBAAA,CAA0B,IAAA,CAAK,IAAI,CAAC,CAAA,CAAA;AAAA,KACtE;AAAA,EACF;AAEA,EAAA,MAAM,KAAA,GAAQ,iBAAiB,IAAI,CAAA;AAKnC,EAAA,MAAM,WAAA,GAAc,MAAM,QAAA,CAAS,SAAA,CAAU,OAAO,EAAE,UAAA,EAAY,KAAA,CAAM,UAAA,EAAY,CAAA;AACpF,EAAA,MAAM,iBAAiB,MAAM,QAAA,CAAS,cAAc,SAAA,CAAU,WAAA,CAAY,IAAI,KAAK,CAAA;AAInF,EAAA,IAAI,KAAK,OAAA,EAAS;AAChB,IAAA,IAAI,cAAA,EAAgB,MAAM,qBAAA,CAAsB,KAAK,CAAA;AACrD,IAAA,OAAO,EAAE,MAAA,EAAQ,QAAA,EAAU,QAAQ,IAAA,CAAK,EAAA,EAAI,cAAc,IAAA,EAAK;AAAA,EACjE;AAGA,EAAA,IAAI,cAAA,EAAgB,MAAM,qBAAA,CAAsB,KAAK,CAAA;AAIrD,EAAA,MAAM,gBAAsC,MAAM,QAAA,CAAS,KAAA,CAAM,gBAAA,CAAiB,KAAK,EAAE,CAAA;AACzF,EAAA,IAAI,CAAC,aAAA,EAAe;AAClB,IAAA,MAAM,IAAI,uBAAA;AAAA,MACR,CAAA,MAAA,EAAS,KAAK,EAAE,CAAA,2EAAA;AAAA,KAClB;AAAA,EACF;AAEA,EAAA,MAAM,GAAA,uBAAU,IAAA,EAAK;AAGrB,EAAA,IAAI,IAAA,CAAK,UAAU,MAAA,EAAW;AAC5B,IAAA,MAAMC,OAAAA,GAAS,aAAA,CAAc,MAAA,EAAW,GAAG,CAAA;AAC3C,IAAA,MAAM,GAAA,GAAM,MAAM,QAAA,CAAS,aAAA,CAAc,MAAA,CAAO;AAAA,MAC9C,YAAY,WAAA,CAAY,EAAA;AAAA,MACxB,QAAQ,IAAA,CAAK,EAAA;AAAA,MACb,aAAa,aAAA,CAAc,OAAA;AAAA,MAC3B,KAAA;AAAA,MACA,MAAA,EAAQ,QAAA;AAAA,MACR,QAAA,EAAU,IAAA;AAAA,MACV,uBAAA,EAAyB,IAAA;AAAA,MACzB,oBAAoBA,OAAAA,CAAO,KAAA;AAAA,MAC3B,kBAAkBA,OAAAA,CAAO,GAAA;AAAA,MACzB,iBAAA,EAAmB;AAAA,KACpB,CAAA;AACD,IAAA,OAAO,EAAE,MAAA,EAAQ,QAAA,EAAU,QAAQ,IAAA,CAAK,EAAA,EAAI,cAAc,GAAA,EAAI;AAAA,EAChE;AAKA,EAAA,MAAM,YAAA,GAAe,aAAA,CAAc,YAAA,CAAa,QAAQ,CAAA;AACxD,EAAA,IAAI,CAAC,YAAA,EAAc;AACjB,IAAA,MAAM,IAAI,uBAAA;AAAA,MACR,CAAA,MAAA,EAAS,IAAA,CAAK,EAAE,CAAA,gBAAA,EAAmB,QAAQ,CAAA,4CAAA;AAAA,KAC7C;AAAA,EACF;AAEA,EAAA,MAAM,MAAA,GAAS,aAAA,CAAc,IAAA,CAAK,KAAA,CAAM,UAAU,GAAG,CAAA;AACrD,EAAA,MAAM,aAAA,GAAgB,MAAM,QAAA,CAAS,aAAA,CAAc,MAAA,CAAO;AAAA,IACxD,YAAY,WAAA,CAAY,EAAA;AAAA,IACxB,QAAQ,IAAA,CAAK,EAAA;AAAA,IACb,aAAa,aAAA,CAAc,OAAA;AAAA,IAC3B,KAAA;AAAA,IACA,MAAA,EAAQ,YAAA;AAAA,IACR,QAAA;AAAA,IACA,uBAAA,EAAyB,IAAA;AAAA,IACzB,oBAAoB,MAAA,CAAO,KAAA;AAAA,IAC3B,kBAAkB,MAAA,CAAO,GAAA;AAAA,IACzB,iBAAA,EAAmB;AAAA,GACpB,CAAA;AAED,EAAA,IAAI,aAAa,QAAA,EAAU;AACzB,IAAA,OAAO,qBAAqB,GAAA,EAAK,QAAA,EAAU,MAAM,YAAA,EAAc,WAAA,EAAa,eAAe,KAAK,CAAA;AAAA,EAClG;AACA,EAAA,OAAO,uBAAuB,GAAA,EAAK,QAAA,EAAU,MAAM,YAAA,EAAc,WAAA,EAAa,eAAe,KAAK,CAAA;AACpG;;;ACpTA,SAAS,SAAS,KAAA,EAAqD;AACrE,EAAA,OAAO,KAAA,KAAU,IAAA,IAAQ,OAAO,KAAA,KAAU,QAAA,IAAY,CAAC,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,GACrE,KAAA,GACD,MAAA;AACN;AAEA,SAAS,WAAA,CAAY,KAA0C,GAAA,EAAiC;AAC9F,EAAA,MAAM,CAAA,GAAI,MAAM,GAAG,CAAA;AACnB,EAAA,OAAO,OAAO,CAAA,KAAM,QAAA,IAAY,CAAA,CAAE,MAAA,GAAS,IAAI,CAAA,GAAI,MAAA;AACrD;AAEA,SAAS,WAAA,CAAY,KAA0C,GAAA,EAAiC;AAC9F,EAAA,MAAM,CAAA,GAAI,MAAM,GAAG,CAAA;AACnB,EAAA,OAAO,OAAO,CAAA,KAAM,QAAA,IAAY,OAAO,QAAA,CAAS,CAAC,IAAI,CAAA,GAAI,MAAA;AAC3D;AAEA,SAAS,SAAA,CAAU,KAA0C,GAAA,EAAkC;AAC7F,EAAA,MAAM,CAAA,GAAI,MAAM,GAAG,CAAA;AACnB,EAAA,OAAO,OAAO,CAAA,KAAM,SAAA,GAAY,CAAA,GAAI,MAAA;AACtC;AAGA,SAAS,aAAa,KAAA,EAAkE;AAEtF,EAAA,IAAI,KAAA,CAAM,QAAA,KAAa,QAAA,EAAU,OAAO,MAAA;AACxC,EAAA,OAAO,QAAA,CAAS,QAAA,CAAS,KAAA,CAAM,IAAI,GAAG,MAAM,CAAA;AAC9C;AAGA,SAAS,SAAS,KAAA,EAAkE;AAElF,EAAA,OAAO,MAAM,QAAA,KAAa,QAAA,GAAW,MAAA,GAAY,QAAA,CAAS,MAAM,IAAI,CAAA;AACtE;AAgBA,SAAS,mBAAmB,KAAA,EAA0C;AACpE,EAAA,IAAI,KAAA,CAAM,aAAa,QAAA,EAAU;AAC/B,IAAA,MAAM,GAAA,GAAM,aAAa,KAAK,CAAA;AAC9B,IAAA,MAAMC,KAAAA,GAAO,QAAA,CAAS,GAAA,EAAK,QAAQ,CAAA;AACnC,IAAA,MAAM,QAAQ,WAAA,CAAY,GAAA,EAAK,qBAAqB,CAAA,IAAK,WAAA,CAAYA,OAAM,eAAe,CAAA;AAI1F,IAAA,MAAM,uBAAA,GACJ,WAAA,CAAY,GAAA,EAAK,cAAc,CAAA,KAC9B,WAAA,CAAY,GAAA,EAAK,QAAQ,CAAA,KAAM,cAAA,GAAiB,WAAA,CAAY,GAAA,EAAK,IAAI,CAAA,GAAI,MAAA,CAAA;AAC5E,IAAA,OAAO;AAAA,MACL,KAAA;AAAA,MACA,UAAA,EAAY,WAAA,CAAYA,KAAAA,EAAM,cAAc,CAAA;AAAA,MAC5C,MAAA,EAAQ,WAAA,CAAYA,KAAAA,EAAM,UAAU,CAAA;AAAA,MACpC;AAAA,KACF;AAAA,EACF;AACA,EAAA,MAAM,IAAA,GAAO,SAAS,KAAK,CAAA;AAC3B,EAAA,MAAM,IAAA,GAAO,QAAA,CAAS,IAAA,EAAM,QAAQ,CAAA;AACpC,EAAA,OAAO;AAAA,IACL,OAAO,WAAA,CAAY,IAAA,EAAM,WAAW,CAAA,IAAK,WAAA,CAAY,MAAM,eAAe,CAAA;AAAA,IAC1E,UAAA,EAAY,WAAA,CAAY,IAAA,EAAM,cAAc,CAAA;AAAA,IAC5C,MAAA,EAAQ,WAAA,CAAY,IAAA,EAAM,UAAU,CAAA;AAAA,IACpC,uBAAA,EAAyB,WAAA,CAAY,IAAA,EAAM,mBAAmB;AAAA,GAChE;AACF;AAEA,SAAS,qBAAA,CAAsB,KAA0B,MAAA,EAA6C;AACpG,EAAA,OAAO,IAAI,QAAA,EAAU,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,OAAO,MAAM,CAAA;AAClD;AASA,eAAe,aAAA,CACb,EAAA,EACA,GAAA,EACA,WAAA,EACgC;AAChC,EAAA,IAAI,CAAC,WAAA,CAAY,UAAA,IAAc,CAAC,WAAA,CAAY,QAAQ,OAAO,IAAA;AAC3D,EAAA,MAAM,IAAA,GAAO,qBAAA,CAAsB,GAAA,EAAK,WAAA,CAAY,MAAM,CAAA;AAC1D,EAAA,IAAI,CAAC,MAAM,OAAO,IAAA;AAClB,EAAA,OAAO,GAAG,aAAA,CAAc,SAAA,CAAU,YAAY,UAAA,EAAY,gBAAA,CAAiB,IAAI,CAAC,CAAA;AAClF;AAUA,SAAS,aAAA,CAAc,WAAgD,GAAA,EAAkD;AACvH,EAAA,MAAM,OAAA,GAAU,QAAA,CAAS,SAAA,GAAY,GAAG,CAAC,CAAA;AACzC,EAAA,MAAM,OAAO,OAAA,EAAS,IAAA;AACtB,EAAA,OAAO,KAAA,CAAM,OAAA,CAAQ,IAAI,CAAA,IAAK,IAAA,CAAK,MAAA,GAAS,CAAA,GAAI,QAAA,CAAS,IAAA,CAAK,CAAC,CAAC,CAAA,GAAI,MAAA;AACtE;AAEA,SAAS,qBAAA,CAAsB,OAA2B,GAAA,EAAsD;AAC9G,EAAA,IAAI,GAAA,KAAQ,QAAW,OAAO,MAAA;AAC9B,EAAA,OAAO,EAAE,KAAA,EAAO,KAAA,KAAU,MAAA,GAAY,IAAI,IAAA,CAAK,KAAA,GAAQ,GAAI,CAAA,GAAI,QAAW,GAAA,EAAK,IAAI,IAAA,CAAK,GAAA,GAAM,GAAI,CAAA,EAAE;AACtG;AAoBA,SAAS,cAAc,KAAA,EAA0D;AAC/E,EAAA,IAAI,KAAA,CAAM,aAAa,QAAA,EAAU;AAC/B,IAAA,MAAM,GAAA,GAAM,aAAa,KAAK,CAAA;AAC9B,IAAA,MAAM,IAAA,GAAO,aAAA,CAAc,GAAA,EAAK,OAAO,CAAA;AACvC,IAAA,IAAI,IAAA,EAAM;AACR,MAAA,MAAM,MAAA,GAAS,qBAAA;AAAA,QACb,WAAA,CAAY,MAAM,sBAAsB,CAAA;AAAA,QACxC,WAAA,CAAY,MAAM,oBAAoB;AAAA,OACxC;AACA,MAAA,IAAI,QAAQ,OAAO,MAAA;AAAA,IACrB;AACA,IAAA,MAAM,IAAA,GAAO,aAAA,CAAc,GAAA,EAAK,OAAO,CAAA;AACvC,IAAA,IAAI,IAAA,EAAM;AACR,MAAA,MAAM,MAAA,GAAS,QAAA,CAAS,IAAA,CAAK,MAAM,CAAA;AACnC,MAAA,MAAM,OAAA,GAAU,sBAAsB,WAAA,CAAY,MAAA,EAAQ,OAAO,CAAA,EAAG,WAAA,CAAY,MAAA,EAAQ,KAAK,CAAC,CAAA;AAC9F,MAAA,IAAI,SAAS,OAAO,OAAA;AAAA,IACtB;AACA,IAAA,OAAO,MAAA;AAAA,EACT;AACA,EAAA,MAAM,IAAA,GAAO,SAAS,KAAK,CAAA;AAC3B,EAAA,MAAM,eAAA,GAAkB,WAAA,CAAY,IAAA,EAAM,mBAAmB,CAAA;AAC7D,EAAA,IAAI,CAAC,iBAAiB,OAAO,MAAA;AAC7B,EAAA,MAAM,GAAA,GAAM,IAAI,IAAA,CAAK,eAAe,CAAA;AACpC,EAAA,IAAI,OAAO,KAAA,CAAM,GAAA,CAAI,OAAA,EAAS,GAAG,OAAO,MAAA;AACxC,EAAA,OAAO,EAAE,KAAA,EAAO,MAAA,EAAW,GAAA,EAAI;AACjC;AAUA,SAAS,kBAAA,CAAmB,MAAA,EAAyB,UAAA,EAAmC,GAAA,EAAiB;AACvG,EAAA,IAAI,MAAA,CAAO,KAAA,EAAO,OAAO,MAAA,CAAO,KAAA;AAChC,EAAA,IAAI,UAAA,IAAc,WAAW,gBAAA,CAAiB,OAAA,MAAa,MAAA,CAAO,GAAA,CAAI,SAAQ,EAAG;AAC/E,IAAA,OAAO,UAAA,CAAW,gBAAA;AAAA,EACpB;AAEA,EAAA,OAAO,GAAA;AACT;AAGA,SAAS,aAAA,CAAc,QAAqC,UAAA,EAAqC;AAC/F,EAAA,OAAO,MAAA,KAAW,UAAa,MAAA,CAAO,GAAA,CAAI,SAAQ,GAAI,UAAA,CAAW,iBAAiB,OAAA,EAAQ;AAC5F;AAcA,IAAM,8BAAA,GAA0F;AAAA,EAC9F,MAAA,EAAQ,QAAA;AAAA,EACR,QAAA,EAAU,UAAA;AAAA,EACV,QAAA,EAAU,UAAA;AAAA,EACV,QAAA,EAAU,UAAA;AAAA,EACV,MAAA,EAAQ,UAAA;AAAA,EACR,MAAA,EAAQ,UAAA;AAAA,EACR,UAAA,EAAY,YAAA;AAAA,EACZ,kBAAA,EAAoB;AACtB,CAAA;AAUA,IAAM,gCAAA,GAA4F;AAAA,EAChG,MAAA,EAAQ,QAAA;AAAA,EACR,cAAA,EAAgB,QAAA;AAAA,EAChB,SAAA,EAAW,UAAA;AAAA,EACX,SAAA,EAAW,UAAA;AAAA,EACX,SAAA,EAAW;AACb,CAAA;AAEA,SAAS,gBAAgB,KAAA,EAAwE;AAC/F,EAAA,IAAI,KAAA,CAAM,aAAa,QAAA,EAAU;AAC/B,IAAA,OAAO,+BAA+B,WAAA,CAAY,YAAA,CAAa,KAAK,CAAA,EAAG,QAAQ,KAAK,EAAE,CAAA;AAAA,EACxF;AACA,EAAA,IAAI,KAAA,CAAM,aAAa,UAAA,EAAY;AACjC,IAAA,OAAO,iCAAiC,WAAA,CAAY,QAAA,CAAS,KAAK,CAAA,EAAG,QAAQ,KAAK,EAAE,CAAA;AAAA,EACtF;AAMA,EAAA,OAAO,MAAA;AACT;AAGA,SAAS,yBAAyB,KAAA,EAAkD;AAClF,EAAA,IAAI,KAAA,CAAM,aAAa,QAAA,EAAU,OAAO,UAAU,YAAA,CAAa,KAAK,GAAG,sBAAsB,CAAA;AAC7F,EAAA,IAAI,MAAM,QAAA,KAAa,UAAA,IAAc,KAAA,CAAM,IAAA,KAAS,0BAA0B,OAAO,IAAA;AACrF,EAAA,OAAO,MAAA;AACT;AAUA,eAAsB,0BAAA,CACpB,UACA,MAAA,EAOe;AACf,EAAA,KAAA,MAAW,SAAA,IAAa,MAAA,CAAO,IAAA,CAAK,QAAA,EAAU;AAC5C,IAAA,IAAI,SAAA,CAAU,SAAS,SAAA,EAAW;AAClC,IAAA,MAAM,IAAA,GAA6B;AAAA,MACjC,OAAO,SAAA,CAAU,KAAA;AAAA,MACjB,eAAe,SAAA,CAAU,KAAA;AAAA,MACzB,QAAQ,MAAA,CAAO,MAAA;AAAA,MACf,MAAA,EAAQ,OAAO,IAAA,CAAK,EAAA;AAAA,MACpB,aAAa,MAAA,CAAO;AAAA,KACtB;AACA,IAAA,MAAM,QAAA,CAAS,SAAS,OAAA,CAAQ,MAAA,CAAO,YAAY,SAAA,CAAU,SAAA,EAAW,MAAA,CAAO,KAAA,EAAO,IAAI,CAAA;AAAA,EAC5F;AACF;AAKA,eAAe,eAAA,CACb,EAAA,EACA,GAAA,EACA,KAAA,EACA,GAAA,EACsB;AACtB,EAAA,MAAM,WAAA,GAAc,mBAAmB,KAAK,CAAA;AAC5C,EAAA,MAAM,MAAA,GAAS,cAAc,KAAK,CAAA;AAClC,EAAA,MAAM,SAAA,GAAY,MAAM,aAAA,CAAc,EAAA,EAAI,KAAK,WAAW,CAAA;AAE1D,EAAA,IAAI,SAAA,EAAW;AAGb,IAAA,IAAI,aAAA,CAAc,QAAQ,SAAS,CAAA,SAAU,EAAE,OAAA,EAAS,KAAA,EAAO,OAAA,EAAS,OAAA,EAAQ;AAChF,IAAA,MAAMC,MAAAA,GAA6B,EAAE,MAAA,EAAQ,QAAA,EAAS;AACtD,IAAA,IAAI,WAAA,CAAY,uBAAA,EAAyBA,MAAAA,CAAM,0BAA0B,WAAA,CAAY,uBAAA;AACrF,IAAA,IAAI,MAAA,EAAQ;AACV,MAAAA,MAAAA,CAAM,kBAAA,GAAqB,kBAAA,CAAmB,MAAA,EAAQ,WAAW,GAAG,CAAA;AACpE,MAAAA,MAAAA,CAAM,mBAAmB,MAAA,CAAO,GAAA;AAAA,IAClC;AACA,IAAA,MAAMC,WAAU,MAAM,EAAA,CAAG,cAAc,MAAA,CAAO,SAAA,CAAU,IAAID,MAAK,CAAA;AACjE,IAAA,OAAO,EAAE,OAAA,EAAS,IAAA,EAAM,YAAA,EAAcC,QAAAA,EAAQ;AAAA,EAChD;AASA,EAAA,IAAI,CAAC,YAAY,KAAA,EAAO,OAAO,EAAE,OAAA,EAAS,KAAA,EAAO,SAAS,YAAA,EAAa;AACvE,EAAA,MAAM,KAAA,GAA6B,EAAE,MAAA,EAAQ,QAAA,EAAS;AACtD,EAAA,IAAI,WAAA,CAAY,uBAAA,EAAyB,KAAA,CAAM,uBAAA,GAA0B,WAAA,CAAY,uBAAA;AACrF,EAAA,IAAI,MAAA,EAAQ;AACV,IAAA,KAAA,CAAM,kBAAA,GAAqB,OAAO,KAAA,IAAS,GAAA;AAC3C,IAAA,KAAA,CAAM,mBAAmB,MAAA,CAAO,GAAA;AAAA,EAClC;AACA,EAAA,MAAM,UAAU,MAAM,EAAA,CAAG,cAAc,MAAA,CAAO,WAAA,CAAY,OAAO,KAAK,CAAA;AACtE,EAAA,OAAO,EAAE,OAAA,EAAS,IAAA,EAAM,YAAA,EAAc,OAAA,EAAQ;AAChD;AAWA,eAAe,qBACb,EAAA,EACA,GAAA,EACA,OACA,GAAA,EACA,YAAA,EACA,iBACA,MAAA,EACsB;AACtB,EAAA,MAAM,WAAA,GAAc,mBAAmB,KAAK,CAAA;AAC5C,EAAA,MAAM,GAAA,GAAM,MAAM,aAAA,CAAc,EAAA,EAAI,KAAK,WAAW,CAAA;AACpD,EAAA,IAAI,CAAC,GAAA,EAAK;AACR,IAAA,MAAA,GAAS;AAAA,MACP,IAAA,EAAM,cAAA;AAAA,MACN,OAAA,EACE,qGAAA;AAAA,MACF,UAAU,KAAA,CAAM,QAAA;AAAA,MAChB,YAAY,KAAA,CAAM,IAAA;AAAA,MAClB,aAAa,KAAA,CAAM;AAAA,KACpB,CAAA;AACD,IAAA,OAAO,EAAE,OAAA,EAAS,KAAA,EAAO,OAAA,EAAS,YAAA,EAAa;AAAA,EACjD;AAEA,EAAA,MAAM,MAAA,GAAS,cAAc,KAAK,CAAA;AAClC,EAAA,IAAI,aAAA,CAAc,QAAQ,GAAG,CAAA,SAAU,EAAE,OAAA,EAAS,KAAA,EAAO,OAAA,EAAS,OAAA,EAAQ;AAE1E,EAAA,MAAM,QAA6B,EAAC;AACpC,EAAA,MAAM,MAAA,GAAS,YAAA,IAAgB,eAAA,CAAgB,KAAK,CAAA;AACpD,EAAA,IAAI,MAAA,QAAc,MAAA,GAAS,MAAA;AAC3B,EAAA,IAAI,MAAA,EAAQ;AACV,IAAA,KAAA,CAAM,kBAAA,GAAqB,kBAAA,CAAmB,MAAA,EAAQ,GAAA,EAAK,GAAG,CAAA;AAC9D,IAAA,KAAA,CAAM,mBAAmB,MAAA,CAAO,GAAA;AAAA,EAClC;AACA,EAAA,MAAM,iBAAA,GAAoB,yBAAyB,KAAK,CAAA;AACxD,EAAA,IAAI,iBAAA,KAAsB,MAAA,EAAW,KAAA,CAAM,iBAAA,GAAoB,iBAAA;AAG/D,EAAA,IAAI,mBAAmB,WAAA,CAAY,MAAA,IAAU,WAAA,CAAY,MAAA,KAAW,IAAI,MAAA,EAAQ;AAC9E,IAAA,MAAM,OAAA,GAAU,qBAAA,CAAsB,GAAA,EAAK,WAAA,CAAY,MAAM,CAAA;AAC7D,IAAA,IAAI,OAAA,EAAS;AACX,MAAA,MAAM,SAAS,MAAM,EAAA,CAAG,KAAA,CAAM,gBAAA,CAAiB,QAAQ,EAAE,CAAA;AACzD,MAAA,MAAM,WAAA,GAAc,MAAA,EAAQ,OAAA,IAAW,GAAA,CAAI,WAAA;AAC3C,MAAA,KAAA,CAAM,SAAS,OAAA,CAAQ,EAAA;AACvB,MAAA,KAAA,CAAM,WAAA,GAAc,WAAA;AACpB,MAAA,MAAM,2BAA2B,EAAA,EAAI;AAAA,QACnC,YAAY,GAAA,CAAI,UAAA;AAAA,QAChB,OAAO,GAAA,CAAI,KAAA;AAAA,QACX,IAAA,EAAM,OAAA;AAAA,QACN,WAAA;AAAA,QACA,MAAA,EAAQ,KAAA,CAAM,kBAAA,IAAsB,GAAA,CAAI;AAAA,OACzC,CAAA;AAAA,IACH;AAAA,EACF;AAEA,EAAA,IAAI,MAAA,CAAO,IAAA,CAAK,KAAK,CAAA,CAAE,MAAA,KAAW,CAAA,EAAG,OAAO,EAAE,OAAA,EAAS,KAAA,EAAO,OAAA,EAAS,UAAA,EAAW;AAClF,EAAA,MAAM,UAAU,MAAM,EAAA,CAAG,cAAc,MAAA,CAAO,GAAA,CAAI,IAAI,KAAK,CAAA;AAC3D,EAAA,OAAO,EAAE,OAAA,EAAS,IAAA,EAAM,YAAA,EAAc,OAAA,EAAQ;AAChD;AAUA,eAAsB,iBAAA,CACpB,GAAA,EACA,KAAA,EACA,OAAA,GAAwB,EAAC,EACH;AACtB,EAAA,MAAM,WAAW,GAAA,CAAI,QAAA;AACrB,EAAA,IAAI,CAAC,QAAA,EAAU;AACb,IAAA,MAAM,IAAI,mBAAA;AAAA,MACR;AAAA,KACF;AAAA,EACF;AACA,EAAA,MAAM,GAAA,GAAM,OAAA,CAAQ,GAAA,oBAAO,IAAI,IAAA,EAAK;AACpC,EAAA,MAAM,SAAS,OAAA,CAAQ,MAAA;AAIvB,EAAA,OAAO,QAAA,CAAS,WAAA,CAAY,OAAO,EAAA,KAA6B;AAC9D,IAAA,MAAM,UAAU,MAAM,EAAA,CAAG,aAAA,CAAc,KAAA,CAAM,MAAM,SAAA,EAAW;AAAA,MAC5D,UAAU,KAAA,CAAM,QAAA;AAAA,MAChB,MAAM,KAAA,CAAM,IAAA;AAAA,MACZ;AAAA,KACD,CAAA;AACD,IAAA,IAAI,CAAC,OAAA,EAAS,OAAO,EAAE,OAAA,EAAS,KAAA,EAAO,SAAS,iBAAA,EAAkB;AAElE,IAAA,IAAI,MAAA;AACJ,IAAA,QAAQ,MAAM,WAAA;AAAa,MACzB,KAAK,mBAAA;AAAA,MACL,KAAK,cAAA;AACH,QAAA,MAAA,GAAS,MAAM,eAAA,CAAgB,EAAA,EAAI,GAAA,EAAK,OAAO,GAAG,CAAA;AAClD,QAAA;AAAA,MACF,KAAK,sBAAA;AAAA,MACL,KAAK,sBAAA;AACH,QAAA,MAAA,GAAS,MAAM,qBAAqB,EAAA,EAAI,GAAA,EAAK,OAAO,GAAA,EAAK,MAAA,EAAW,MAAM,MAAM,CAAA;AAChF,QAAA;AAAA,MACF,KAAK,uBAAA;AACH,QAAA,MAAA,GAAS,MAAM,qBAAqB,EAAA,EAAI,GAAA,EAAK,OAAO,GAAA,EAAK,UAAA,EAAY,OAAO,MAAM,CAAA;AAClF,QAAA;AAAA,MACF,KAAK,wBAAA;AACH,QAAA,MAAA,GAAS,MAAM,qBAAqB,EAAA,EAAI,GAAA,EAAK,OAAO,GAAA,EAAK,UAAA,EAAY,OAAO,MAAM,CAAA;AAClF,QAAA;AAAA,MACF;AAIE,QAAA,MAAA,GAAS,EAAE,OAAA,EAAS,KAAA,EAAO,OAAA,EAAS,UAAA,EAAW;AAAA;AAanD,IAAA,IAAI,MAAA,CAAO,YAAY,YAAA,EAAc;AACnC,MAAA,MAAM,EAAA,CAAG,aAAA,CAAc,WAAA,CAAY,KAAA,CAAM,SAAS,CAAA;AAAA,IACpD;AACA,IAAA,OAAO,MAAA;AAAA,EACT,CAAC,CAAA;AACH;ACxkBA,IAAM,qBAAA,GAAwB,GAAA;AAW9B,SAAS,eAAA,CAAgB,UAAkB,SAAA,EAA4B;AACrE,EAAA,MAAM,QAAA,GAAW,MAAA,CAAO,IAAA,CAAK,SAAA,EAAW,MAAM,CAAA;AAC9C,EAAA,OAAO,SAAS,MAAA,KAAW,QAAA,CAAS,MAAA,IAAU,eAAA,CAAgB,UAAU,QAAQ,CAAA;AAClF;AAUO,SAAS,YAAA,CACd,OAAA,EACA,eAAA,EACA,aAAA,EACA,OAAA,EACS;AAKT,EAAA,IAAI,OAAO,aAAA,KAAkB,QAAA,IAAY,aAAA,CAAc,MAAA,KAAW,GAAG,OAAO,KAAA;AAC5E,EAAA,IAAI,OAAO,eAAA,KAAoB,QAAA,IAAY,eAAA,CAAgB,MAAA,KAAW,GAAG,OAAO,KAAA;AAChF,EAAA,IAAI,OAAO,OAAA,KAAY,QAAA,IAAY,EAAE,OAAA,YAAmB,aAAa,OAAO,KAAA;AAO5E,EAAA,IAAI,YAAA;AACJ,EAAA,MAAM,aAAuB,EAAC;AAC9B,EAAA,KAAA,MAAW,OAAA,IAAW,eAAA,CAAgB,KAAA,CAAM,GAAG,CAAA,EAAG;AAChD,IAAA,MAAM,IAAA,GAAO,QAAQ,IAAA,EAAK;AAC1B,IAAA,MAAM,EAAA,GAAK,IAAA,CAAK,OAAA,CAAQ,GAAG,CAAA;AAC3B,IAAA,IAAI,EAAA,KAAO,IAAI,OAAO,KAAA;AACtB,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,KAAA,CAAM,CAAA,EAAG,EAAE,CAAA;AAC/B,IAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,KAAA,CAAM,EAAA,GAAK,CAAC,CAAA;AAC/B,IAAA,IAAI,WAAW,GAAA,EAAK;AAClB,MAAA,IAAI,YAAA,KAAiB,QAAW,OAAO,KAAA;AACvC,MAAA,YAAA,GAAe,KAAA;AAAA,IACjB,CAAA,MAAA,IAAW,WAAW,IAAA,EAAM;AAC1B,MAAA,UAAA,CAAW,KAAK,KAAK,CAAA;AAAA,IACvB;AAAA,EACF;AACA,EAAA,IAAI,iBAAiB,MAAA,IAAa,CAAC,QAAQ,IAAA,CAAK,YAAY,GAAG,OAAO,KAAA;AACtE,EAAA,IAAI,UAAA,CAAW,MAAA,KAAW,CAAA,EAAG,OAAO,KAAA;AAEpC,EAAA,MAAM,SAAA,GAAY,OAAO,YAAY,CAAA;AACrC,EAAA,MAAM,MAAA,GAAS,OAAA,EAAS,GAAA,KAAQ,MAAA,GAAY,OAAA,CAAQ,GAAA,EAAI,GAAI,IAAA,CAAK,KAAA,CAAM,IAAA,CAAK,GAAA,EAAI,GAAI,GAAI,CAAA;AACxF,EAAA,MAAM,YAAA,GAAe,SAAS,YAAA,IAAgB,qBAAA;AAC9C,EAAA,MAAM,eAAA,GAAkB,IAAA,CAAK,GAAA,CAAI,MAAA,GAAS,SAAS,CAAA,IAAK,YAAA;AAIxD,EAAA,MAAM,IAAA,GAAO,UAAA,CAAW,QAAA,EAAU,aAAa,CAAA;AAC/C,EAAA,IAAA,CAAK,MAAA,CAAO,CAAA,EAAG,YAAY,CAAA,CAAA,CAAG,CAAA;AAC9B,EAAA,IAAA,CAAK,OAAO,OAAO,CAAA;AACnB,EAAA,MAAM,WAAW,MAAA,CAAO,IAAA,CAAK,KAAK,MAAA,CAAO,KAAK,GAAG,MAAM,CAAA;AAKvD,EAAA,IAAI,OAAA,GAAU,KAAA;AACd,EAAA,KAAA,MAAW,aAAa,UAAA,EAAY;AAClC,IAAA,OAAA,GAAU,eAAA,CAAgB,QAAA,EAAU,SAAS,CAAA,IAAK,OAAA;AAAA,EACpD;AAEA,EAAA,OAAO,eAAA,IAAmB,OAAA;AAC5B;;;ACzEA,IAAM,mBAAA,GAA2C,EAAE,QAAA,EAAU,MAAA,EAAW,UAAU,MAAA,EAAU;AAkE5F,SAAS,WAAW,OAAA,EAAsC;AACxD,EAAA,OAAO,OAAO,YAAY,QAAA,GAAW,OAAA,GAAU,IAAI,WAAA,EAAY,CAAE,OAAO,OAAO,CAAA;AACjF;AAEA,SAASC,UAAS,KAAA,EAAqD;AACrE,EAAA,OAAO,KAAA,KAAU,IAAA,IAAQ,OAAO,KAAA,KAAU,QAAA,IAAY,CAAC,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,GACrE,KAAA,GACD,MAAA;AACN;AAGA,SAAS,iBAAA,CAAkB,MAA+B,QAAA,EAAmC;AAC3F,EAAA,MAAM,SAAA,GACJ,QAAA,KAAa,aAAA,GACR,IAAA,CAAK,IAAA,IAAQ,IAAA,CAAK,KAAA,GACnB,QAAA,KAAa,QAAA,GACX,IAAA,CAAK,IAAA,GACL,IAAA,CAAK,KAAA;AACb,EAAA,OAAO,OAAO,SAAA,KAAc,QAAA,GAAW,SAAA,GAAY,SAAA;AACrD;AAGA,SAAS,iBAAiB,IAAA,EAAmD;AAC3E,EAAA,OAAO,OAAO,KAAK,EAAA,KAAO,QAAA,IAAY,KAAK,EAAA,CAAG,MAAA,GAAS,CAAA,GAAI,IAAA,CAAK,EAAA,GAAK,MAAA;AACvE;AASA,SAAS,gBAAA,CACP,QAAA,EACA,UAAA,EACA,IAAA,EACA,SAAA,EACQ;AAKR,EAAA,MAAM,WAAA,GACJ,MAAM,EAAA,IACN,IAAA,EAAM,aACN,IAAA,EAAM,aAAA,IACN,IAAA,EAAM,iBAAA,IACN,IAAA,EAAM,aAAA;AACR,EAAA,MAAM,SACJ,WAAA,KAAgB,MAAA,IAAa,gBAAgB,IAAA,GAAO,MAAA,CAAO,WAAW,CAAA,GAAI,EAAA;AAC5E,EAAA,MAAM,SAAS,OAAO,IAAA,EAAM,MAAA,KAAW,QAAA,GAAW,KAAK,MAAA,GAAS,EAAA;AAChE,EAAA,IAAI,aAAa,aAAA,EAAe;AAC9B,IAAA,IAAI,SAAA,KAAc,QAAW,OAAO,SAAA;AACpC,IAAA,OAAO,CAAA,EAAG,MAAM,CAAA,CAAA,EAAI,MAAM,CAAA,CAAA;AAAA,EAC5B;AACA,EAAA,IAAI,QAAA,KAAa,QAAA,IAAY,SAAA,KAAc,MAAA,EAAW,OAAO,SAAA;AAC7D,EAAA,OAAO,UAAA,CAAW,QAAQ,CAAA,CAAE,MAAA,CAAO,CAAA,EAAG,UAAU,CAAA,CAAA,EAAI,MAAM,CAAA,CAAA,EAAI,MAAM,CAAA,CAAE,CAAA,CAAE,OAAO,KAAK,CAAA;AACtF;AAYO,SAAS,eAAe,MAAA,EAA4C;AACzE,EAAA,MAAM,EAAE,SAAS,OAAA,EAAS,QAAA,EAAU,SAAS,MAAA,EAAQ,MAAA,EAAQ,SAAQ,GAAI,MAAA;AAIzE,EAAA,IAAI,CAAC,MAAA,CAAO,EAAE,OAAA,EAAS,OAAA,EAAS,CAAA,EAAG;AACjC,IAAA,MAAM,IAAI,iCAAiC,wCAAA,EAA0C;AAAA,MACnF,QAAA;AAAA,MACA,WAAA,EAAa;AAAA,KACd,CAAA;AAAA,EACH;AAEA,EAAA,IAAI,MAAA;AACJ,EAAA,IAAI;AACF,IAAA,MAAA,GAAS,IAAA,CAAK,KAAA,CAAM,UAAA,CAAW,OAAO,CAAC,CAAA;AAAA,EACzC,SAAS,KAAA,EAAO;AACd,IAAA,MAAM,IAAI,iCAAiC,iCAAA,EAAmC;AAAA,MAC5E,QAAA;AAAA,MACA,WAAA,EAAa,KAAA;AAAA,MACb;AAAA,KACD,CAAA;AAAA,EACH;AAEA,EAAA,MAAM,IAAA,GAAOA,SAAAA,CAAS,MAAM,CAAA,IAAK,EAAC;AAClC,EAAA,MAAM,OAAO,IAAA,CAAK,IAAA;AAClB,EAAA,MAAM,UAAA,GAAa,iBAAA,CAAkB,IAAA,EAAM,QAAQ,CAAA;AAOnD,EAAA,MAAM,WAAA,GAAc,kBAAA,CAAmB,QAAA,EAAU,OAAA,EAAS,YAAY,IAAI,CAAA;AAC1E,EAAA,MAAM,UAAA,GAAaA,UAAS,IAAI,CAAA;AAUhC,EAAA,IAAI,QAAA,KAAa,QAAA,IAAY,OAAO,UAAA,EAAY,WAAW,QAAA,EAAU;AACnE,IAAA,eAAA,CAAgB,QAAA,EAAU,OAAA,EAAS,UAAA,CAAW,MAAA,EAAQ,MAAM,CAAA;AAAA,EAC9D;AACA,EAAA,MAAM,YACJ,QAAA,KAAa,aAAA,IAAiB,aAAa,QAAA,GAAW,gBAAA,CAAiB,IAAI,CAAA,GAAI,MAAA;AACjF,EAAA,MAAM,SAAA,GAAY,gBAAA,CAAiB,QAAA,EAAU,UAAA,EAAY,YAAY,SAAS,CAAA;AAK9E,EAAA,MAAM,IAAA,GAAoC;AAAA,IACxC,QAAA;AAAA,IACA,IAAA,EAAM,UAAA;AAAA,IACN,WAAA;AAAA,IACA,IAAA;AAAA,IACA,GAAA,EAAK,MAAA;AAAA,IACL,SAAA;AAAA,IACA,GAAI,SAAA,KAAc,MAAA,GAAY,EAAE,EAAA,EAAI,SAAA,KAAc;AAAC,GACrD;AAEA,EAAA,OAAO;AAAA,IACL,GAAG,IAAA;AAAA;AAAA;AAAA;AAAA,IAIH,KAAA,EAAO,CAAC,OAAA,KAAY,iBAAA,CAAkB,OAAA,IAAW,mBAAA,EAAqB,IAAA,EAAM,EAAE,MAAA,EAAQ,GAAG,OAAA,EAAS;AAAA,GACpG;AACF","file":"chunk-CGQ73I4I.js","sourcesContent":["/**\n * `subscribe()` — the billing module's entry point.\n *\n * - `BillingSync` pushes `pw_plans` rows with `provider_refs` — {@link subscribe}\n * only READS them (`database.plans.getActiveVersion`); it never pushes/syncs.\n * Callers that haven't run a sync yet can seed `pw_plans` rows directly via\n * `database.plans.pushVersion(...)`.\n * - `event.apply()` flips the `incomplete` row this module creates to `active`\n * once the provider confirms payment — see the \"seams for the webhook\n * handler\" note below for exactly what correlation data this module leaves\n * behind.\n * - `check`/`report` reuse {@link resolvePlanGroup} and the\n * {@link BillingContext} shape (or a lookalike) to resolve a customer's\n * effective plan/group the same way `subscribe` does.\n *\n * Everything here operates ONLY through the `DatabaseAdapter` contract\n * (`../db/index`) and the existing Surface A resource classes\n * (`StripeClient`/`PaystackClient`) — no new provider HTTP calls, no changes\n * to the DB contract.\n *\n * ── Seams left for the webhook handler (webhook → billing state) ───────────\n * The `incomplete` row this module creates has `providerSubscriptionRef: null`\n * — the real provider subscription id doesn't exist until checkout completes.\n * Correlation data left for the webhook handler to pick up:\n * - Stripe: the Checkout Session's `client_reference_id` AND\n * `metadata.pwv_reference` are both set to the local `pw_subscriptions` row\n * id (`incompleteRow.id`). `checkout.session.completed` carries both plus\n * the resulting `subscription` id, so the handler can resolve\n * `incompleteRow.id → sub_xxx` directly from that one event without a new\n * DB query method (`subscriptions.getActive` only surfaces the active-set\n * statuses today — `incomplete` rows aren't listable by (customer, group),\n * so event-carried correlation is the only path available).\n * - Paystack: `transactions.initialize` is called with OUR OWN `reference` set\n * to the local `pw_subscriptions` row id — Paystack echoes it back on every\n * subsequent event/verify call, so `subscription.create`/`charge.success`\n * payloads carry it as `data.reference` verbatim.\n *\n * ── Spec-silent decision: a plan without an explicit `group` ────────────────\n * `default: true` plans always have a `group` (`plan()` enforces it), but a\n * non-default plan may not. `pw_subscriptions.group` is a required, non-null\n * column, so *something* has to fill it. The spec doesn't say what — the\n * conservative reading kept here is: a groupless plan is its own singleton\n * group (its `id`), so subscribing to it never contends with any other plan\n * (see {@link resolvePlanGroup}).\n *\n * ── Spec-silent decision: a second `subscribe()` while a checkout is pending ─\n * `incomplete` sits OUTSIDE the partial-unique active-status set\n * (`active`/`past_due`/`trialing`), and the `DatabaseAdapter` contract exposes\n * no way to look up a customer's pending `incomplete` row by (customer,\n * group) — only `subscriptions.getActive` exists, and it deliberately\n * excludes `incomplete`. Superseding the prior pending row would need a new\n * adapter read method, which this module deliberately avoids adding. The\n * conservative, contract-respecting behavior implemented below: each paid\n * `subscribe()` call with no ACTIVE-set row in the group creates a NEW\n * `incomplete` row — this never violates the partial-unique index (it\n * doesn't apply to `incomplete`), but it can leave an orphaned `incomplete`\n * row behind if the customer abandons a checkout and re-subscribes.\n * Provider-customer creation is still idempotent either way\n * (`ensureProviderCustomer` below) — the \"two subscribes → one provider\n * customer\" acceptance criterion holds regardless of this row-level gap.\n * Flagged rather than silently narrowed.\n *\n * ── Spec-silent decision: Paystack requires an email `subscribe()` doesn't take ─\n * Paystack's customer-create AND transaction-initialize endpoints both require\n * `email` (not optional — `paystack/schemas/customers.ts`,\n * `paystack/schemas/transactions.ts`), but `subscribe()`'s signature carries\n * no `email` field, and there's no public `payweave.customers.*` surface to\n * set one. The conservative path taken here: read `email` off the LOCAL\n * `pw_customers` row (nullable) — callers who need Paystack billing must have\n * set it via their OWN `database` adapter reference\n * (`database.customers.upsert({ externalId, email })`) at some point before\n * calling `subscribe`; `upsert` preserves an existing email when a later call\n * omits it. Missing email + Paystack throws a clear, actionable\n * {@link PayweaveValidationError} rather than silently failing at the\n * provider.\n */\nimport type { PayweaveProviderKey, ResolvedProduct } from \"../core/config\";\nimport { PayweaveConfigError, PayweaveProviderError, PayweaveValidationError } from \"../core/errors\";\nimport type { DatabaseAdapter, PwCustomer, PwPlanVersion, PwSubscription } from \"../db/index\";\nimport type { PaystackClient } from \"../paystack/client\";\nimport type { StripeClient } from \"../stripe/client\";\nimport { advance } from \"./period\";\n\n/**\n * Providers with a working `subscribe()` mapping. Flutterwave's payment-plan\n * flow is ⚠️ pending build-time doc verification — it is not billing-capable\n * yet, so `subscribe()` rejects it with a typed capability error rather than\n * silently no-op'ing or guessing a mapping.\n */\nexport const BILLING_CAPABLE_PROVIDERS = [\"stripe\", \"paystack\"] as const;\n\n/** A provider key {@link BILLING_CAPABLE_PROVIDERS} lists. */\nexport type BillingCapableProvider = (typeof BILLING_CAPABLE_PROVIDERS)[number];\n\nfunction isBillingCapableProvider(provider: PayweaveProviderKey): provider is BillingCapableProvider {\n return (BILLING_CAPABLE_PROVIDERS as readonly string[]).includes(provider);\n}\n\n/**\n * Everything `subscribe()` (and, by the same shape, `event.apply()` and\n * `check`/`report`) needs out of `createPayweave`'s resolved config and\n * mounted provider surfaces. Deliberately NOT the full `PayweaveClient` —\n * just the billing-relevant slice, so this module never imports `../index`\n * (avoids a circular import back into the facade).\n */\nexport interface BillingContext {\n readonly database: DatabaseAdapter | undefined;\n readonly products: readonly ResolvedProduct[] | undefined;\n readonly providers: readonly PayweaveProviderKey[];\n readonly defaultProvider: PayweaveProviderKey;\n readonly stripe: StripeClient | undefined;\n readonly paystack: PaystackClient | undefined;\n}\n\n/**\n * `subscribe()`'s input. Mirrors `SubscribeInput<C>`\n * in `src/index.ts` field-for-field, but non-generic — the compile-time\n * `PlanIds<C>`/`ConfiguredProvider<C>` narrowing is the client facade's job\n * (it widens back to these plain types before calling in here).\n */\nexport interface SubscribeInput {\n /** YOUR user id (external id) — Payweave maps it to provider customers. */\n customerId: string;\n planId: string;\n /** Defaults to `defaultProvider`; must be a configured, billing-capable provider. */\n provider?: PayweaveProviderKey;\n /** Checkout redirect target on success (provider-dependent). */\n successUrl?: string;\n /** Checkout redirect target on cancel (provider-dependent). */\n cancelUrl?: string;\n}\n\n/**\n * `subscribe()`'s discriminated result — mirrors `SubscribeResult` in\n * `src/index.ts` exactly (kept as a separate identical declaration rather than\n * an import to avoid a circular module dependency on the facade).\n */\nexport type SubscribeResult =\n | { status: \"checkout\"; checkoutUrl: string; reference: string }\n | { status: \"active\"; planId: string; subscription: PwSubscription | null };\n\n/**\n * A plan's group. `default: true` plans always have one (`plan()`\n * enforces it), but a non-default plan may not — see the module doc comment's\n * \"groupless plan\" decision: it becomes its own singleton group.\n */\nexport function resolvePlanGroup(plan: ResolvedProduct): string {\n return plan.group ?? plan.id;\n}\n\n/**\n * Nominal period bounds for a subscription row created OUTSIDE a provider's\n * own billing cycle math: a free (non-default) plan has no `interval` at all,\n * and a freshly-created `incomplete` paid row doesn't have Stripe/Paystack's\n * real cycle yet either (Stripe's pinned API version puts that on the\n * subscription's ITEMS — `items.data[].current_period_start/end`, NOT the\n * subscription itself; the webhook handler overwrites these placeholders with\n * the provider's real bounds once it confirms payment).\n * `pw_subscriptions.currentPeriodStart/End` are non-null columns, so a\n * placeholder is required either way; anchoring it at `now` for one\n * plan-interval (defaulting to `month` when the plan has none) is the\n * conservative reading.\n */\nfunction nominalPeriod(interval: \"month\" | \"year\" | undefined, now: Date): { start: Date; end: Date } {\n const startMs = now.getTime();\n return { start: new Date(startMs), end: new Date(advance(startMs, interval ?? \"month\", 1)) };\n}\n\nfunction findPlan(\n products: readonly ResolvedProduct[] | undefined,\n planId: string,\n): ResolvedProduct | undefined {\n return products?.find((p) => p.id === planId);\n}\n\nfunction groupExclusivityError(group: string): PayweaveValidationError {\n // Exact message asserted verbatim by tests.\n return new PayweaveValidationError(`customer already has an active plan in group '${group}'`);\n}\n\n/**\n * Create (once) or reuse the per-provider customer for `customerRow`:\n * consult `providerIds` BEFORE ever creating one remotely, and persist a\n * fresh id via `linkProviderRef` — this is what makes \"two subscribes → one\n * provider customer\" hold regardless of how many times `subscribe()` runs.\n */\nasync function ensureProviderCustomer(\n ctx: BillingContext,\n database: DatabaseAdapter,\n provider: BillingCapableProvider,\n customerRow: PwCustomer,\n externalId: string,\n): Promise<string> {\n const existing = customerRow.providerIds[provider];\n if (existing !== undefined) return existing;\n\n if (provider === \"stripe\") {\n /* istanbul ignore next -- defensive: unreachable — `provider` was already\n confirmed configured (and therefore mounted) before this is called. */\n if (!ctx.stripe) throw new PayweaveConfigError(\"stripe is not configured on this client.\");\n const created = await ctx.stripe.customers.create({\n ...(customerRow.email !== null ? { email: customerRow.email } : {}),\n metadata: { pwv_reference: customerRow.id },\n });\n await database.customers.linkProviderRef(externalId, \"stripe\", created.id);\n return created.id;\n }\n\n /* istanbul ignore next -- defensive: same as the stripe branch above. */\n if (!ctx.paystack) throw new PayweaveConfigError(\"paystack is not configured on this client.\");\n if (customerRow.email === null) {\n throw new PayweaveValidationError(\n \"paystack billing requires a customer email, but this customer has none on file — call \" +\n \"`database.customers.upsert({ externalId, email })` against your own adapter reference \" +\n \"before subscribing (`subscribe()`'s input doesn't carry an email, and Paystack's \" +\n \"customer/transaction APIs require one).\",\n );\n }\n const created = await ctx.paystack.customers.create({\n email: customerRow.email,\n metadata: { pwv_reference: customerRow.id },\n });\n const customerCode = created.data.customer_code;\n if (customerCode === undefined) {\n throw new PayweaveProviderError(\"paystack created a customer without a customer_code\", {\n provider: \"paystack\",\n });\n }\n await database.customers.linkProviderRef(externalId, \"paystack\", customerCode);\n return customerCode;\n}\n\n/** Stripe leg of the paid-plan branch — checkout session in `subscription` mode against the synced price. */\nasync function createStripeCheckout(\n ctx: BillingContext,\n database: DatabaseAdapter,\n plan: ResolvedProduct,\n providerRefs: Record<string, string>,\n customerRow: PwCustomer,\n incompleteRow: PwSubscription,\n input: SubscribeInput,\n): Promise<SubscribeResult> {\n const priceId = providerRefs.priceId;\n if (priceId === undefined) {\n throw new PayweaveValidationError(\n `plan \"${plan.id}\" has no pushed stripe price — run \\`payweave push\\`.`,\n );\n }\n /* istanbul ignore next -- defensive: `provider` was confirmed configured before this is called. */\n if (!ctx.stripe) throw new PayweaveConfigError(\"stripe is not configured on this client.\");\n\n const providerCustomerId = await ensureProviderCustomer(\n ctx,\n database,\n \"stripe\",\n customerRow,\n input.customerId,\n );\n\n const session = await ctx.stripe.checkout.sessions.create({\n mode: \"subscription\",\n customer: providerCustomerId,\n line_items: [{ price: priceId, quantity: 1 }],\n // Correlates the resulting `checkout.session.completed` event back to\n // this local row (see the module doc comment's \"seams for the webhook\n // handler\" note).\n client_reference_id: incompleteRow.id,\n metadata: { pwv_reference: incompleteRow.id, pwv_plan: plan.id, pwv_customer: customerRow.id },\n ...(input.successUrl !== undefined ? { success_url: input.successUrl } : {}),\n ...(input.cancelUrl !== undefined ? { cancel_url: input.cancelUrl } : {}),\n });\n\n if (!session.url) {\n throw new PayweaveProviderError(\"stripe created a checkout session without a hosted URL\", {\n provider: \"stripe\",\n raw: session,\n });\n }\n return { status: \"checkout\", checkoutUrl: session.url, reference: session.id };\n}\n\n/** Paystack leg of the paid-plan branch — initialize a transaction against the synced plan code. */\nasync function createPaystackCheckout(\n ctx: BillingContext,\n database: DatabaseAdapter,\n plan: ResolvedProduct,\n providerRefs: Record<string, string>,\n customerRow: PwCustomer,\n incompleteRow: PwSubscription,\n input: SubscribeInput,\n): Promise<SubscribeResult> {\n const planCode = providerRefs.planCode;\n if (planCode === undefined) {\n throw new PayweaveValidationError(\n `plan \"${plan.id}\" has no pushed paystack plan code — run \\`payweave push\\`.`,\n );\n }\n /* istanbul ignore next -- defensive: `provider` was confirmed configured before this is called. */\n if (!ctx.paystack) throw new PayweaveConfigError(\"paystack is not configured on this client.\");\n\n await ensureProviderCustomer(ctx, database, \"paystack\", customerRow, input.customerId);\n // `ensureProviderCustomer` may have just linked the ref — the email guard\n // below is still needed even when the provider customer already existed\n // (a previous `subscribe()` call could have created it before an email was\n // ever set on the local row).\n if (customerRow.email === null) {\n throw new PayweaveValidationError(\n \"paystack billing requires a customer email, but this customer has none on file — call \" +\n \"`database.customers.upsert({ externalId, email })` against your own adapter reference \" +\n \"before subscribing (`subscribe()`'s input doesn't carry an email, and Paystack's \" +\n \"customer/transaction APIs require one).\",\n );\n }\n\n // `plan.price` is already resolved to integer minor units (kobo) — never\n // re-converted here (AGENTS.md golden rule 7).\n const amountMinor = plan.price?.amount;\n /* istanbul ignore next -- defensive: only paid plans reach this function. */\n if (amountMinor === undefined) {\n throw new PayweaveValidationError(`plan \"${plan.id}\" has no price to charge.`);\n }\n\n const res = await ctx.paystack.transactions.initialize({\n email: customerRow.email,\n amount: amountMinor,\n plan: planCode,\n // Our OWN reference — Paystack echoes it back on every subsequent\n // event/verify call, so it doubles as the local row's stable correlation\n // key for the webhook handler (see the module doc comment).\n reference: incompleteRow.id,\n metadata: { pwv_reference: incompleteRow.id, pwv_plan: plan.id, pwv_customer: customerRow.id },\n });\n\n return { status: \"checkout\", checkoutUrl: res.data.authorization_url, reference: res.data.reference };\n}\n\n/**\n * The `subscribe()` flow. See the module doc\n * comment for the spec-silent decisions this implementation makes.\n */\nexport async function subscribe(ctx: BillingContext, input: SubscribeInput): Promise<SubscribeResult> {\n // Billing methods throw PayweaveConfigError at call time when `database`\n // is missing, regardless of what the type system could prove statically\n // (products-without-database is already impossible once config parse\n // succeeds — core/config.ts rule 5 — but a client with NEITHER `database`\n // NOR `products` configured still exposes `subscribe` on its surface, and\n // this is the runtime half of that guard).\n const database = ctx.database;\n if (!database) {\n throw new PayweaveConfigError(\n \"payweave.subscribe() needs a database — pass a payweave/db/* adapter to createPayweave().\",\n );\n }\n\n const plan = findPlan(ctx.products, input.planId);\n if (!plan) {\n throw new PayweaveValidationError(\n `unknown plan \"${input.planId}\" — configure it in \\`products\\` and run \\`payweave push\\`.`,\n );\n }\n\n const provider = input.provider ?? ctx.defaultProvider;\n if (!ctx.providers.includes(provider)) {\n throw new PayweaveConfigError(\n `provider \"${provider}\" is not configured on this client — configured: ${ctx.providers.join(\", \")}.`,\n );\n }\n if (!isBillingCapableProvider(provider)) {\n throw new PayweaveConfigError(\n `provider \"${provider}\" does not support billing (subscribe) — ` +\n `billing-capable providers: ${BILLING_CAPABLE_PROVIDERS.join(\", \")}.`,\n );\n }\n\n const group = resolvePlanGroup(plan);\n\n // Upsert the local customer row unconditionally (cheap, no provider call);\n // provider CUSTOMER creation is deferred to the paid branch below, so\n // default/free subscribes make zero provider calls.\n const customerRow = await database.customers.upsert({ externalId: input.customerId });\n const existingActive = await database.subscriptions.getActive(customerRow.id, group);\n\n // Default plan: no-op absent an active sub in the group; group-exclusivity\n // error if one exists (paid or free — one active plan per group at a time).\n if (plan.default) {\n if (existingActive) throw groupExclusivityError(group);\n return { status: \"active\", planId: plan.id, subscription: null };\n }\n\n // Any other plan in an already-occupied group: same error.\n if (existingActive) throw groupExclusivityError(group);\n\n // Resolve the plan's active pushed version (needed for BOTH the free and\n // paid branches: `pw_subscriptions.planVersion` is required either way).\n const pushedVersion: PwPlanVersion | null = await database.plans.getActiveVersion(plan.id);\n if (!pushedVersion) {\n throw new PayweaveValidationError(\n `plan \"${plan.id}\" has no pushed version — run \\`payweave push\\` to sync your products.`,\n );\n }\n\n const now = new Date();\n\n // Free non-default plan: local activation, zero provider calls.\n if (plan.price === undefined) {\n const period = nominalPeriod(undefined, now);\n const row = await database.subscriptions.create({\n customerId: customerRow.id,\n planId: plan.id,\n planVersion: pushedVersion.version,\n group,\n status: \"active\",\n provider: null,\n providerSubscriptionRef: null,\n currentPeriodStart: period.start,\n currentPeriodEnd: period.end,\n cancelAtPeriodEnd: false,\n });\n return { status: \"active\", planId: plan.id, subscription: row };\n }\n\n // Paid plan: create the provider checkout using the pushed provider_refs;\n // the local row starts `incomplete` and is flipped to `active` by the\n // webhook handler's `event.apply()`, never here.\n const providerRefs = pushedVersion.providerRefs[provider];\n if (!providerRefs) {\n throw new PayweaveValidationError(\n `plan \"${plan.id}\" has no pushed ${provider} provider refs — run \\`payweave push\\`.`,\n );\n }\n\n const period = nominalPeriod(plan.price.interval, now);\n const incompleteRow = await database.subscriptions.create({\n customerId: customerRow.id,\n planId: plan.id,\n planVersion: pushedVersion.version,\n group,\n status: \"incomplete\",\n provider,\n providerSubscriptionRef: null,\n currentPeriodStart: period.start,\n currentPeriodEnd: period.end,\n cancelAtPeriodEnd: false,\n });\n\n if (provider === \"stripe\") {\n return createStripeCheckout(ctx, database, plan, providerRefs, customerRow, incompleteRow, input);\n }\n return createPaystackCheckout(ctx, database, plan, providerRefs, customerRow, incompleteRow, input);\n}\n","/**\n * `event.apply()` — webhook → billing state. Mirrors `subscribe.ts`'s/\n * `usage.ts`'s \"non-generic module, generic facade\" split with `src/index.ts`\n * — this file never imports `../index` (avoids a circular import back into\n * the facade) and, like those two, is written ONCE against the\n * `DatabaseAdapter` contract (`../db/index`) — no new provider HTTP calls, no\n * DB contract changes.\n *\n * `webhooks/index.ts`'s `constructEvent` attaches `.apply` to every\n * `WebhookEvent` it returns; this module is what that closure calls.\n *\n * ── The idempotency gate ─────────────────────────────────────────────────────\n * `webhookEvents.claim(dedupeKey, …)` IS the entire idempotency mechanism — no\n * second dedupe layer here. `claim` → `false` means an identical\n * (`dedupeKey`) event already applied, or another caller is mid-apply right\n * now: `apply()` returns `{ applied: false, skipped: \"already-applied\" }`\n * without touching billing state. `claim` + the state mutation + `markApplied`\n * all run inside ONE `database.transaction(fn)` call (\"claim + side effects +\n * markApplied run in ONE transaction so a failed apply rolls the claim back\")\n * — an adapter with real transactions (the sqlite adapter's `BEGIN\n * IMMEDIATE`/`COMMIT`/`ROLLBACK`) makes a thrown error immediately\n * re-claimable, never waiting out `staleClaimAfterMs`; an adapter without one\n * falls back to exactly the stale-claim story. This also gives \"local\"\n * idempotency for free (\"calling it twice on the same event object in one\n * process applies once\"): the second call's `claim()` sees the already-applied\n * row and returns `false` — no separate in-process latch needed.\n *\n * ── Correlating an event back to a `pw_subscriptions` row ───────────────────\n * The `DatabaseAdapter` contract exposes exactly two subscription reads:\n * `getActive(customerId, group)` (the active-SET statuses only —\n * `active`/`past_due`/`trialing`) and nothing keyed by id or by provider\n * reference. Every correlation this module does rides data the EVENT ITSELF\n * carries, per `subscribe.ts`'s own \"seams left for the webhook handler\" doc\n * comment:\n *\n * - Stripe: `data.object.client_reference_id` and/or `data.object.metadata.\n * pwv_reference` name the local row id directly (set on the Checkout\n * Session by `subscribe.ts`); `data.object.metadata.pwv_customer`/\n * `pwv_plan` name the local customer row id / config plan id (SAME\n * session-level `metadata` object — `subscribe.ts` sets all three keys\n * together, https://docs.stripe.com/api/checkout/sessions/object\n * `metadata`/`client_reference_id`).\n * - Paystack: `data.reference` (echoed back verbatim from `transactions.\n * initialize`'s own `reference`, https://paystack.com/docs/api/transaction/\n * #initialize) and/or `data.metadata.pwv_reference`/`pwv_customer`/\n * `pwv_plan` (same echo).\n *\n * KNOWN LIMITATION (flagged, not fixed here): Stripe session-level `metadata`\n * is NOT copied onto the Subscription object Stripe creates from that\n * checkout (only an explicit `subscription_data.metadata` at\n * session-creation time would be — a `subscribe.ts` change, out of scope).\n * So today, a bare `customer.subscription.updated`/`.deleted` event for\n * STRIPE that carries no `pwv_*` metadata of its own cannot be correlated to a\n * row by metadata; this module still resolves it via the (`pwv_customer`,\n * `pwv_plan`) → `getActive(customerId, group)` path WHEN that metadata IS\n * present (Paystack already echoes it; a future `subscribe.ts` revision that\n * adds `subscription_data.metadata` would make Stripe's lifecycle events\n * resolvable the same way) and otherwise treats the event as `\"unresolved\"` —\n * a safe no-op, never a guess at the wrong row.\n *\n * ── Out-of-order guard ───────────────────────────────────────────────────────\n * `subscription.*`/`invoice.*` events can arrive out of send order (provider\n * redelivery, network reordering). Two protections, both keyed off the\n * CURRENTLY STORED row (read via `getActive` before writing — the FIRST\n * `incomplete → active` activation has no prior \"real\" state to protect and\n * is exempt, see {@link applyActivation}):\n * 1. A row already OUTSIDE the active set (i.e. `canceled`) is invisible to\n * `getActive` — a late `subscription.updated` after `subscription.canceled`\n * therefore finds no row at all and safely no-ops (`\"unresolved\"`) rather\n * than resurrecting it.\n * 2. Within the active set, period fields only move FORWARD: an event whose\n * period end is EARLIER than the row's current `currentPeriodEnd` is\n * treated as stale redelivery and skipped (`\"stale\"`) — status/other\n * fields are not touched either, so a late event never partially regresses\n * a row a newer event already advanced.\n *\n * ── Plan-change balance reset ────────────────────────────────────────────────\n * `subscription.updated` events tell us the row's *current* plan via the same\n * `pwv_plan` metadata used for correlation. When it differs from the stored\n * row's `planId`, that's a genuine plan change (`pw_subscriptions.planId` is\n * patchable — the patch schema explicitly allows it): this module patches\n * `planId`/`planVersion` on the row AND calls\n * {@link resetBalancesForPlanChange} for every METERED feature the new plan\n * includes, zeroing `used` and re-seeding `limit`/`resetInterval`/`anchor`\n * from the new plan (`balances.resetTo`). {@link resetBalancesForPlanChange}\n * is exported standalone so a conformance scenario can also call/wire it\n * directly.\n */\nimport { PayweaveConfigError } from \"../core/errors\";\nimport type { Logger } from \"../core/logger\";\nimport type { DatabaseAdapter, PwFeatureBalanceInit, PwSubscription, PwSubscriptionPatch } from \"../db/index\";\nimport type { ResolvedProduct } from \"../core/config\";\nimport { resolvePlanGroup, type BillingContext } from \"./subscribe\";\nimport type { WebhookProvider } from \"../webhooks/index\";\n\n/**\n * Everything {@link applyWebhookEvent} needs out of `createPayweave`'s\n * resolved config — a slice of {@link BillingContext} (only `database` +\n * `products` are ever touched; `subscribe`/`check`/`report`'s wider context\n * carries provider clients this module never calls). `src/index.ts` passes\n * the SAME `billingContext` object it already builds for the billing surface —\n * structurally compatible, so no new object is constructed there.\n */\nexport type BillingApplyContext = Pick<BillingContext, \"database\" | \"products\">;\n\n/**\n * The slice of a constructed `WebhookEvent` (`../webhooks/index`) this module\n * needs. Deliberately NOT the full interface — `WebhookEvent.apply` itself\n * closes over one of these, so the full type (which carries `apply`) would be\n * circular to construct from inside `constructEvent`.\n */\nexport interface WebhookEventForApply {\n readonly provider: WebhookProvider;\n readonly type: string;\n readonly unifiedType: string;\n readonly data: unknown;\n readonly dedupeKey: string;\n}\n\n/** Why {@link ApplyResult.applied} is `false`. Absent when `applied` is `true`. */\nexport type ApplySkipReason = \"already-applied\" | \"unmapped\" | \"unresolved\" | \"stale\";\n\n/** Result of one {@link applyWebhookEvent} call. */\nexport interface ApplyResult {\n /** `true` only when this call performed the state mutation. */\n applied: boolean;\n skipped?: ApplySkipReason;\n /** The mutated row, when `applied` is `true`; `undefined` otherwise. */\n subscription?: PwSubscription;\n}\n\n/** Options accepted by {@link applyWebhookEvent} (and, minus `logger`, by `WebhookEvent.apply`). */\nexport interface ApplyOptions {\n /** Injected clock — testability (fake redelivery timing, out-of-order fixtures). Default `new Date()`. */\n now?: Date | undefined;\n /** Optional injected logger for drift reporting (never `console.*`). */\n logger?: Logger | undefined;\n}\n\n// ── Small, provider-agnostic payload readers ────────────────────────────────\n\nfunction asRecord(value: unknown): Record<string, unknown> | undefined {\n return value !== null && typeof value === \"object\" && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : undefined;\n}\n\nfunction stringField(obj: Record<string, unknown> | undefined, key: string): string | undefined {\n const v = obj?.[key];\n return typeof v === \"string\" && v.length > 0 ? v : undefined;\n}\n\nfunction numberField(obj: Record<string, unknown> | undefined, key: string): number | undefined {\n const v = obj?.[key];\n return typeof v === \"number\" && Number.isFinite(v) ? v : undefined;\n}\n\nfunction boolField(obj: Record<string, unknown> | undefined, key: string): boolean | undefined {\n const v = obj?.[key];\n return typeof v === \"boolean\" ? v : undefined;\n}\n\n/** Stripe's nested resource (`data.object`, https://docs.stripe.com/api/events/object) — `undefined` for every other provider's flat `data`. */\nfunction stripeObject(event: WebhookEventForApply): Record<string, unknown> | undefined {\n /* istanbul ignore next -- defensive: every call site already gates on `event.provider === \"stripe\"` first. */\n if (event.provider !== \"stripe\") return undefined;\n return asRecord(asRecord(event.data)?.object);\n}\n\n/** The flat `data` object every non-Stripe provider uses. */\nfunction flatData(event: WebhookEventForApply): Record<string, unknown> | undefined {\n /* istanbul ignore next -- defensive: every call site already gates on `event.provider !== \"stripe\"` first. */\n return event.provider === \"stripe\" ? undefined : asRecord(event.data);\n}\n\n// ── Correlation (module doc comment above) ──────────────────────────────────\n\n/** What we could learn about which local row this event concerns. */\ninterface Correlation {\n /** The `pw_subscriptions.id` `subscribe()` stamped onto the event (rowId-level correlation). */\n rowId: string | undefined;\n /** The `pw_customers.id` `subscribe()` stamped onto the event. */\n customerId: string | undefined;\n /** The config plan id the event concerns (correlation AND, on updates, plan-change detection). */\n planId: string | undefined;\n /** The provider's own subscription reference, when the event carries one. */\n providerSubscriptionRef: string | undefined;\n}\n\nfunction resolveCorrelation(event: WebhookEventForApply): Correlation {\n if (event.provider === \"stripe\") {\n const obj = stripeObject(event);\n const meta = asRecord(obj?.metadata);\n const rowId = stringField(obj, \"client_reference_id\") ?? stringField(meta, \"pwv_reference\");\n // Checkout Session: `subscription` is the created Subscription's id\n // (string, unexpanded — https://docs.stripe.com/api/checkout/sessions/object).\n // A Subscription event's OWN `id` IS that reference.\n const providerSubscriptionRef =\n stringField(obj, \"subscription\") ??\n (stringField(obj, \"object\") === \"subscription\" ? stringField(obj, \"id\") : undefined);\n return {\n rowId,\n customerId: stringField(meta, \"pwv_customer\"),\n planId: stringField(meta, \"pwv_plan\"),\n providerSubscriptionRef,\n };\n }\n const data = flatData(event);\n const meta = asRecord(data?.metadata);\n return {\n rowId: stringField(data, \"reference\") ?? stringField(meta, \"pwv_reference\"),\n customerId: stringField(meta, \"pwv_customer\"),\n planId: stringField(meta, \"pwv_plan\"),\n providerSubscriptionRef: stringField(data, \"subscription_code\"),\n };\n}\n\nfunction resolveGroupForPlanId(ctx: BillingApplyContext, planId: string): ResolvedProduct | undefined {\n return ctx.products?.find((p) => p.id === planId);\n}\n\n/**\n * Find the row a lifecycle event concerns via `getActive(customerId, group)`\n * — the ONLY id-free read the `DatabaseAdapter` contract exposes (module doc\n * comment). Returns `null` when the correlation metadata is absent OR the\n * row genuinely isn't in the active set right now (already canceled — the\n * out-of-order protection, not a bug).\n */\nasync function findActiveRow(\n tx: DatabaseAdapter,\n ctx: BillingApplyContext,\n correlation: Correlation,\n): Promise<PwSubscription | null> {\n if (!correlation.customerId || !correlation.planId) return null;\n const plan = resolveGroupForPlanId(ctx, correlation.planId);\n if (!plan) return null;\n return tx.subscriptions.getActive(correlation.customerId, resolvePlanGroup(plan));\n}\n\n// ── Period extraction (CRITICAL finding: Stripe period lives on subscription ITEMS) ──\n\ninterface ExtractedPeriod {\n /** `undefined` when the event's shape carries no derivable period start (module doc comment — Paystack). */\n start: Date | undefined;\n end: Date;\n}\n\nfunction firstListItem(container: Record<string, unknown> | undefined, key: string): Record<string, unknown> | undefined {\n const wrapper = asRecord(container?.[key]);\n const list = wrapper?.data;\n return Array.isArray(list) && list.length > 0 ? asRecord(list[0]) : undefined;\n}\n\nfunction periodFromUnixSeconds(start: number | undefined, end: number | undefined): ExtractedPeriod | undefined {\n if (end === undefined) return undefined;\n return { start: start !== undefined ? new Date(start * 1000) : undefined, end: new Date(end * 1000) };\n}\n\n/**\n * Read the real billing period off an event's payload, per provider/shape.\n * Stripe: a Subscription-shaped `data.object` (subscription.created/updated)\n * carries it on `items.data[0].current_period_start/end` — NOT on the\n * subscription itself on the pinned API version\n * (https://docs.stripe.com/api/subscriptions/object, CRITICAL finding (c));\n * an Invoice-shaped `data.object` (invoice.paid/payment_failed) carries the\n * equivalent on `lines.data[0].period.start/end`\n * (https://docs.stripe.com/api/invoices/object). A Checkout-Session-shaped\n * `data.object` (checkout.session.completed) carries neither — `undefined`,\n * which callers treat as \"no period info on THIS event\" rather than an error.\n * Paystack: no start/end period exists anywhere in the API — only\n * `next_payment_date` (the CURRENT cycle's end,\n * https://paystack.com/docs/api/subscription/, the exact field already\n * modeled by `src/paystack/schemas/plans.ts`'s `subscription` schema) — the\n * period START is left `undefined` for the caller to derive conservatively\n * (see {@link resolvePeriodStart}).\n */\nfunction extractPeriod(event: WebhookEventForApply): ExtractedPeriod | undefined {\n if (event.provider === \"stripe\") {\n const obj = stripeObject(event);\n const item = firstListItem(obj, \"items\");\n if (item) {\n const period = periodFromUnixSeconds(\n numberField(item, \"current_period_start\"),\n numberField(item, \"current_period_end\"),\n );\n if (period) return period;\n }\n const line = firstListItem(obj, \"lines\");\n if (line) {\n const period = asRecord(line.period);\n const derived = periodFromUnixSeconds(numberField(period, \"start\"), numberField(period, \"end\"));\n if (derived) return derived;\n }\n return undefined;\n }\n const data = flatData(event);\n const nextPaymentDate = stringField(data, \"next_payment_date\");\n if (!nextPaymentDate) return undefined;\n const end = new Date(nextPaymentDate);\n if (Number.isNaN(end.getTime())) return undefined;\n return { start: undefined, end };\n}\n\n/**\n * Fill in an `undefined` period start conservatively: contiguous with the\n * stored row when possible, else `now`. Both call sites only reach this AFTER\n * {@link isStalePeriod} already confirmed `period.end >= currentRow.\n * currentPeriodEnd` for the SAME row, so the `now` fallback below is a\n * defensive backstop (a `null` `currentRow`, or the contiguity check somehow\n * failing despite that invariant) rather than a reachable outcome today.\n */\nfunction resolvePeriodStart(period: ExtractedPeriod, currentRow: PwSubscription | null, now: Date): Date {\n if (period.start) return period.start;\n if (currentRow && currentRow.currentPeriodEnd.getTime() <= period.end.getTime()) {\n return currentRow.currentPeriodEnd;\n }\n /* istanbul ignore next -- defensive backstop, see the doc comment above. */\n return now;\n}\n\n/** `true` when `period` is strictly OLDER than what `currentRow` already stores — the out-of-order guard. */\nfunction isStalePeriod(period: ExtractedPeriod | undefined, currentRow: PwSubscription): boolean {\n return period !== undefined && period.end.getTime() < currentRow.currentPeriodEnd.getTime();\n}\n\n// ── Status/flag mapping (spec-silent — the unified event is named, not every native status string) ──\n\n/**\n * Stripe Subscription `status` → `pw_subscriptions.status`\n * (https://docs.stripe.com/api/subscriptions/object, verified 2026-07-13).\n * `unpaid`/`paused` are dunning-adjacent, non-terminal states — closest local\n * analog is `past_due` (still billing-relevant, not yet given up on);\n * `incomplete_expired` never became a real subscription — closest analog is\n * `canceled` (nothing to keep active). `subscription.canceled` (the\n * `customer.subscription.deleted` event) always forces `\"canceled\"` directly\n * rather than trusting this table (see {@link applyCancellation}).\n */\nconst STRIPE_SUBSCRIPTION_STATUS_MAP: Readonly<Record<string, PwSubscriptionPatch[\"status\"]>> = {\n active: \"active\",\n trialing: \"trialing\",\n past_due: \"past_due\",\n canceled: \"canceled\",\n unpaid: \"past_due\",\n paused: \"past_due\",\n incomplete: \"incomplete\",\n incomplete_expired: \"canceled\",\n};\n\n/**\n * Paystack subscription `status` → `pw_subscriptions.status` (the same\n * `subscription` schema's `status: z.string()`,\n * `src/paystack/schemas/plans.ts`). `non-renewing` stays `active` (still\n * billing normally until the period ends — `cancelAtPeriodEnd` carries the\n * \"won't renew\" signal separately, see {@link extractCancelAtPeriodEnd});\n * `attention` (a failed-charge/needs-attention state) maps to `past_due`.\n */\nconst PAYSTACK_SUBSCRIPTION_STATUS_MAP: Readonly<Record<string, PwSubscriptionPatch[\"status\"]>> = {\n active: \"active\",\n \"non-renewing\": \"active\",\n attention: \"past_due\",\n completed: \"canceled\",\n cancelled: \"canceled\",\n};\n\nfunction mapNativeStatus(event: WebhookEventForApply): PwSubscriptionPatch[\"status\"] | undefined {\n if (event.provider === \"stripe\") {\n return STRIPE_SUBSCRIPTION_STATUS_MAP[stringField(stripeObject(event), \"status\") ?? \"\"];\n }\n if (event.provider === \"paystack\") {\n return PAYSTACK_SUBSCRIPTION_STATUS_MAP[stringField(flatData(event), \"status\") ?? \"\"];\n }\n // `unified/mappings.ts`'s FLUTTERWAVE_EVENT_MAP maps nothing to\n // `subscription.created`/`.updated` (only `.cancelled`) — this function\n // is only ever called for those two unified\n // types, so a Flutterwave event never reaches it in practice today.\n /* istanbul ignore next -- defensive: unreachable given the current unified mapping tables. */\n return undefined;\n}\n\n/** Stripe's `cancel_at_period_end`; Paystack's `subscription.not_renew` native event (no boolean field — the event name IS the signal). */\nfunction extractCancelAtPeriodEnd(event: WebhookEventForApply): boolean | undefined {\n if (event.provider === \"stripe\") return boolField(stripeObject(event), \"cancel_at_period_end\");\n if (event.provider === \"paystack\" && event.type === \"subscription.not_renew\") return true;\n return undefined;\n}\n\n// ── Plan-change balance reset ─────────────────────────────────────────────────\n\n/**\n * Reset every METERED feature `plan` includes for `customerId`/`group` to the\n * plan's own limits (`balances.resetTo` — \"plan changes\").\n * Exported standalone (not just called from {@link applyWebhookEvent}) so\n * a conformance scenario can also wire/call it directly.\n */\nexport async function resetBalancesForPlanChange(\n database: DatabaseAdapter,\n params: {\n customerId: string;\n group: string;\n plan: ResolvedProduct;\n planVersion: number;\n anchor: Date;\n },\n): Promise<void> {\n for (const inclusion of params.plan.includes) {\n if (inclusion.type !== \"metered\") continue;\n const init: PwFeatureBalanceInit = {\n limit: inclusion.limit,\n resetInterval: inclusion.reset,\n anchor: params.anchor,\n planId: params.plan.id,\n planVersion: params.planVersion,\n };\n await database.balances.resetTo(params.customerId, inclusion.featureId, params.group, init);\n }\n}\n\n// ── The three state-mutating branches ───────────────────────────────────────\n\n/** `payment.succeeded` / `invoice.paid`: flip an `incomplete` row to `active`, or refresh an already-active row's real period on renewal. */\nasync function applyActivation(\n tx: DatabaseAdapter,\n ctx: BillingApplyContext,\n event: WebhookEventForApply,\n now: Date,\n): Promise<ApplyResult> {\n const correlation = resolveCorrelation(event);\n const period = extractPeriod(event);\n const activeRow = await findActiveRow(tx, ctx, correlation);\n\n if (activeRow) {\n // Renewal on an already-active row — the out-of-order guard applies here\n // (this is NOT the first activation).\n if (isStalePeriod(period, activeRow)) return { applied: false, skipped: \"stale\" };\n const patch: PwSubscriptionPatch = { status: \"active\" };\n if (correlation.providerSubscriptionRef) patch.providerSubscriptionRef = correlation.providerSubscriptionRef;\n if (period) {\n patch.currentPeriodStart = resolvePeriodStart(period, activeRow, now);\n patch.currentPeriodEnd = period.end;\n }\n const updated = await tx.subscriptions.update(activeRow.id, patch);\n return { applied: true, subscription: updated };\n }\n\n // No active-set row found for this correlation — the expected shape for the\n // FIRST activation of the `incomplete` row `subscribe()` created (`incomplete`\n // is outside the active set by design). Resolve it via the\n // row id `subscribe()` left on the event (module doc comment). No guard: an\n // `incomplete` row's period is only ever `subscribe()`'s nominal placeholder\n // (`subscribe.ts`'s `nominalPeriod`), so there is no \"real\" prior state to\n // protect against regression.\n if (!correlation.rowId) return { applied: false, skipped: \"unresolved\" };\n const patch: PwSubscriptionPatch = { status: \"active\" };\n if (correlation.providerSubscriptionRef) patch.providerSubscriptionRef = correlation.providerSubscriptionRef;\n if (period) {\n patch.currentPeriodStart = period.start ?? now;\n patch.currentPeriodEnd = period.end;\n }\n const updated = await tx.subscriptions.update(correlation.rowId, patch);\n return { applied: true, subscription: updated };\n}\n\n/**\n * `subscription.created`/`subscription.updated`/`subscription.canceled`/\n * `invoice.payment_failed`: update an already-active row.\n * `forcedStatus` pins the target status for the events whose UNIFIED TYPE\n * alone is authoritative (`canceled`, `payment_failed`) regardless of\n * whatever native `status` string the payload carries; `undefined` (plain\n * `subscription.created`/`.updated`) reads the real status via\n * {@link mapNativeStatus}.\n */\nasync function applyLifecycleUpdate(\n tx: DatabaseAdapter,\n ctx: BillingApplyContext,\n event: WebhookEventForApply,\n now: Date,\n forcedStatus: PwSubscriptionPatch[\"status\"] | undefined,\n allowPlanChange: boolean,\n logger: Logger | undefined,\n): Promise<ApplyResult> {\n const correlation = resolveCorrelation(event);\n const row = await findActiveRow(tx, ctx, correlation);\n if (!row) {\n logger?.({\n type: \"schema_drift\",\n message:\n \"billing apply: could not correlate a subscription lifecycle event to a local row — safe no-op.\",\n provider: event.provider,\n nativeType: event.type,\n unifiedType: event.unifiedType,\n });\n return { applied: false, skipped: \"unresolved\" };\n }\n\n const period = extractPeriod(event);\n if (isStalePeriod(period, row)) return { applied: false, skipped: \"stale\" };\n\n const patch: PwSubscriptionPatch = {};\n const status = forcedStatus ?? mapNativeStatus(event);\n if (status) patch.status = status;\n if (period) {\n patch.currentPeriodStart = resolvePeriodStart(period, row, now);\n patch.currentPeriodEnd = period.end;\n }\n const cancelAtPeriodEnd = extractCancelAtPeriodEnd(event);\n if (cancelAtPeriodEnd !== undefined) patch.cancelAtPeriodEnd = cancelAtPeriodEnd;\n\n // A genuine plan change (module doc comment).\n if (allowPlanChange && correlation.planId && correlation.planId !== row.planId) {\n const newPlan = resolveGroupForPlanId(ctx, correlation.planId);\n if (newPlan) {\n const pushed = await tx.plans.getActiveVersion(newPlan.id);\n const planVersion = pushed?.version ?? row.planVersion;\n patch.planId = newPlan.id;\n patch.planVersion = planVersion;\n await resetBalancesForPlanChange(tx, {\n customerId: row.customerId,\n group: row.group,\n plan: newPlan,\n planVersion,\n anchor: patch.currentPeriodStart ?? row.currentPeriodStart,\n });\n }\n }\n\n if (Object.keys(patch).length === 0) return { applied: false, skipped: \"unmapped\" };\n const updated = await tx.subscriptions.update(row.id, patch);\n return { applied: true, subscription: updated };\n}\n\n// ── Entry point ──────────────────────────────────────────────────────────────\n\n/**\n * Idempotently apply one webhook event's billing-state transition.\n * Throws {@link PayweaveConfigError} when no\n * `database` is configured — mirrors `subscribe`/`check`/`report`'s \"the\n * method always exists, calling it without a database always throws\" pattern.\n */\nexport async function applyWebhookEvent(\n ctx: BillingApplyContext,\n event: WebhookEventForApply,\n options: ApplyOptions = {},\n): Promise<ApplyResult> {\n const database = ctx.database;\n if (!database) {\n throw new PayweaveConfigError(\n \"event.apply() needs a database — pass a payweave/db/* adapter to createPayweave().\",\n );\n }\n const now = options.now ?? new Date();\n const logger = options.logger;\n\n // Module doc comment: claim + side effects + markApplied run in ONE\n // transaction (see the design rule in the module doc comment).\n return database.transaction(async (tx): Promise<ApplyResult> => {\n const claimed = await tx.webhookEvents.claim(event.dedupeKey, {\n provider: event.provider,\n type: event.type,\n now,\n });\n if (!claimed) return { applied: false, skipped: \"already-applied\" };\n\n let result: ApplyResult;\n switch (event.unifiedType) {\n case \"payment.succeeded\":\n case \"invoice.paid\":\n result = await applyActivation(tx, ctx, event, now);\n break;\n case \"subscription.created\":\n case \"subscription.updated\":\n result = await applyLifecycleUpdate(tx, ctx, event, now, undefined, true, logger);\n break;\n case \"subscription.canceled\":\n result = await applyLifecycleUpdate(tx, ctx, event, now, \"canceled\", false, logger);\n break;\n case \"invoice.payment_failed\":\n result = await applyLifecycleUpdate(tx, ctx, event, now, \"past_due\", false, logger);\n break;\n default:\n // Unmapped/irrelevant (transfers, refunds, disputes, \"unknown\", …) —\n // ALWAYS a safe no-op; never throw on an unmapped type, mirroring\n // `unified/mappings.ts`'s own \"never drop, never throw on unmapped\" rule.\n result = { applied: false, skipped: \"unmapped\" };\n }\n\n // `\"unresolved\"` is deliberately NOT marked applied (spec-silent decision\n // — the conservative reading): the correlation data an event carries can never change on\n // redelivery of that SAME event, so retrying it is only useful if a LATER\n // apply (e.g. a subsequent event, or `subscribe()` finishing a slightly\n // delayed row write) makes it resolvable — leaving the claim un-applied\n // keeps it re-claimable after `staleClaimAfterMs` instead of silently and\n // permanently dropping an event we never acted on. Every other outcome\n // (a real mutation, `\"stale\"` — genuinely superseded, never worth another\n // try, or `\"unmapped\"` — nothing to ever do for this type) is terminal:\n // mark it applied so it never re-triggers this work again.\n if (result.skipped !== \"unresolved\") {\n await tx.webhookEvents.markApplied(event.dedupeKey);\n }\n return result;\n });\n}\n","/**\n * Stripe webhook verification. HMAC-SHA256 over\n * `${t}.${rawBody}` keyed with the endpoint signing secret (`whsec_*`), hex\n * digest. The `stripe-signature` header carries `t=<unix seconds>` plus one\n * `v1=<hex hmac>` per active secret (several during a key roll — accept if ANY\n * matches); non-`v1` schemes (`v0` is a fake, test-only scheme) are ignored to\n * prevent downgrade attacks. Timing-safe with a length-guard on EVERY `v1`\n * candidate; replays outside ±`toleranceSec` (default 300s, Stripe's library\n * default of 5 minutes) are rejected in both directions. Fails closed on a\n * missing/malformed header, missing/non-numeric `t`, no `v1`, or empty secret.\n *\n * Docs: https://docs.stripe.com/webhooks#verify-manually\n */\nimport { createHmac, timingSafeEqual } from \"node:crypto\";\n\n/** Stripe's documented library default tolerance: 5 minutes. */\nconst DEFAULT_TOLERANCE_SEC = 300;\n\n/** Options for {@link verifyStripe}. */\nexport interface VerifyStripeOptions {\n /** Max allowed `|now − t|` in seconds. Default `300` (Stripe's 5 minutes). */\n toleranceSec?: number;\n /** Clock returning Unix SECONDS — injectable for tests. Default: `Date.now()/1000`. */\n now?: () => number;\n}\n\n/** Sibling length-guarded `timingSafeEqual` (paystack/flutterwave-v4 pattern). */\nfunction matchesExpected(expected: Buffer, candidate: string): boolean {\n const received = Buffer.from(candidate, \"utf8\");\n return expected.length === received.length && timingSafeEqual(expected, received);\n}\n\n/**\n * @param rawBody - Exact received bytes (never a re-serialized object).\n * @param signatureHeader - Value of the `stripe-signature` header.\n * @param webhookSecret - Endpoint signing secret (`whsec_*`) — NOT the API key.\n * @param options - Tolerance override / injectable clock (tests only).\n * @returns `true` iff any `v1` signature matches AND `t` is within tolerance;\n * `false` otherwise (never throws).\n */\nexport function verifyStripe(\n rawBody: string | Uint8Array,\n signatureHeader: string | null | undefined,\n webhookSecret: string,\n options?: VerifyStripeOptions,\n): boolean {\n // Runtime type guards: the \"never throws\" contract must hold for plain-JS\n // callers passing wrong types — fail closed instead. NOTE: an empty STRING\n // body stays accepted (Stripe signs `${t}.` over it); only wrong types and\n // an empty secret are rejected here.\n if (typeof webhookSecret !== \"string\" || webhookSecret.length === 0) return false;\n if (typeof signatureHeader !== \"string\" || signatureHeader.length === 0) return false;\n if (typeof rawBody !== \"string\" && !(rawBody instanceof Uint8Array)) return false;\n\n // Parse `t=...,v1=...[,v1=...,v0=...]`. Whole items are trimmed because\n // proxies may normalize comma-separated headers with `, `; whitespace INSIDE\n // an item (e.g. `t =123`) still fails closed. Unknown schemes are skipped\n // (never verified); an item without `=` or a duplicate `t` is malformed —\n // fail closed.\n let timestampRaw: string | undefined;\n const candidates: string[] = [];\n for (const rawItem of signatureHeader.split(\",\")) {\n const item = rawItem.trim();\n const eq = item.indexOf(\"=\");\n if (eq === -1) return false;\n const scheme = item.slice(0, eq);\n const value = item.slice(eq + 1);\n if (scheme === \"t\") {\n if (timestampRaw !== undefined) return false;\n timestampRaw = value;\n } else if (scheme === \"v1\") {\n candidates.push(value);\n }\n }\n if (timestampRaw === undefined || !/^\\d+$/.test(timestampRaw)) return false;\n if (candidates.length === 0) return false;\n\n const timestamp = Number(timestampRaw);\n const nowSec = options?.now !== undefined ? options.now() : Math.floor(Date.now() / 1000);\n const toleranceSec = options?.toleranceSec ?? DEFAULT_TOLERANCE_SEC;\n const withinTolerance = Math.abs(nowSec - timestamp) <= toleranceSec;\n\n // Sign `${t}.` + the raw body bytes exactly as received (two updates so a\n // Uint8Array body is never decoded/re-encoded).\n const hmac = createHmac(\"sha256\", webhookSecret);\n hmac.update(`${timestampRaw}.`);\n hmac.update(rawBody);\n const expected = Buffer.from(hmac.digest(\"hex\"), \"utf8\");\n\n // Compare EVERY candidate timing-safely — no early exit, so observable time\n // never depends on which (if any) signature matched. Non-hex or wrong-length\n // candidates simply fail the comparison (fail closed).\n let matched = false;\n for (const candidate of candidates) {\n matched = matchesExpected(expected, candidate) || matched;\n }\n\n return withinTolerance && matched;\n}\n","// webhooks/ — signature-verification primitives + the `constructEvent`\n// normalizer the SDK facade delegates to. The four pure, timing-safe\n// verify functions stay the security boundary; `constructEvent` reuses the\n// facade's already-wired verify dispatch (never re-implements verification),\n// then parses the RAW bytes and normalizes via `unified/mappings.ts`. The\n// multi-provider header dispatcher for `createPayweave` lives in `dispatch.ts`.\n// Public subpath: `payweave/webhooks`.\nimport { createHash } from \"node:crypto\";\nimport { PayweaveWebhookVerificationError } from \"../core/errors\";\nimport type { HeaderLookup, UnifiedEvent } from \"../core/provider\";\nimport type { Logger } from \"../core/logger\";\nimport {\n toUnifiedEventType,\n toUnifiedStatus,\n type MappingProvider,\n type MappingVersion,\n} from \"../unified/mappings\";\nimport {\n applyWebhookEvent,\n type ApplyOptions,\n type ApplyResult,\n type BillingApplyContext,\n} from \"../products/apply\";\n\n/** A `database`-less billing context — `event.apply()` always exists (mirrors `subscribe`/`check`/`report`), but calling it without a database throws `PayweaveConfigError`. */\nconst NO_DATABASE_CONTEXT: BillingApplyContext = { database: undefined, products: undefined };\n\nexport { verifyPaystack } from \"./paystack\";\nexport { verifyFlutterwaveV3 } from \"./flutterwave\";\nexport { verifyFlutterwaveV4 } from \"./flutterwave-v4\";\nexport { verifyStripe } from \"./stripe\";\n\n/**\n * Providers whose webhooks `constructEvent` can normalize. Stripe's\n * events are normalized through the SAME `STRIPE_EVENT_MAP`/\n * `STRIPE_EVENT_STATUS_SPLIT_MAP` tables (`unified/mappings.ts`) that\n * `toUnifiedEventType` already used for every other provider —\n * unmapped Stripe events still fall through to `unifiedType: \"unknown\"`,\n * exactly like an unmapped Paystack/Flutterwave event.\n */\nexport type WebhookProvider = MappingProvider | \"stripe\";\n\n/**\n * A verified + normalized webhook event. Extends the core {@link UnifiedEvent}\n * with the provider-supplied webhook `id` (Flutterwave and Stripe send one;\n * Paystack does not — see {@link ConstructEventParams} for the dedupe-key\n * scheme).\n */\nexport interface WebhookEvent extends UnifiedEvent {\n provider: WebhookProvider;\n /** Provider webhook id when present (Flutterwave `wbk_*` / v3 payload id; Stripe `evt_*`). */\n id?: string;\n /** Always populated — the idempotency key consumers should dedupe on. */\n dedupeKey: string;\n /**\n * Idempotently apply this event's billing-state transition — subscription\n * status/period flips, driven by `webhookEvents.claim`'s once-only gate.\n * ALWAYS present (mirrors `payweave.subscribe`/`check`/`report`: the method\n * exists on every client; without a `database` configured, CALLING it\n * throws {@link PayweaveConfigError} rather than the method being absent).\n * See `src/products/apply.ts` for the full contract.\n */\n apply(options?: ApplyOptions): Promise<ApplyResult>;\n}\n\n/**\n * Inputs for {@link constructEvent}. `verify` is the facade's own dispatch\n * closure (provider + version already bound), so verification stays in the\n * timing-safe primitives and a v3 client can never accept a v4 signature (its\n * header is absent) and vice versa.\n */\nexport interface ConstructEventParams {\n /** Exact received bytes — never a re-serialized object. */\n rawBody: string | Uint8Array;\n /** `Headers` or a plain/array header map; looked up case-insensitively. */\n headers: HeaderLookup;\n provider: WebhookProvider;\n version: MappingVersion;\n /** The facade's verify dispatch (throws {@link PayweaveConfigError} for FLW without `webhookSecret`). */\n verify(input: { rawBody: string | Uint8Array; headers: HeaderLookup }): boolean;\n /** Optional injected logger for status drift. */\n logger?: Logger | undefined;\n /**\n * The `database`/`products` slice `event.apply()` runs against.\n * `undefined` when the webhooks namespace was built without a database —\n * `apply()` still exists on the returned event but throws\n * {@link PayweaveConfigError} when called.\n */\n billing?: BillingApplyContext | undefined;\n}\n\nfunction decodeBody(rawBody: string | Uint8Array): string {\n return typeof rawBody === \"string\" ? rawBody : new TextDecoder().decode(rawBody);\n}\n\nfunction asRecord(value: unknown): Record<string, unknown> | undefined {\n return value !== null && typeof value === \"object\" && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : undefined;\n}\n\n/** Native event name: Paystack uses `event`; FLW v4 uses `type`, v3 uses `event`; Stripe uses `type`. */\nfunction extractNativeType(root: Record<string, unknown>, provider: WebhookProvider): string {\n const candidate =\n provider === \"flutterwave\"\n ? (root.type ?? root.event)\n : provider === \"stripe\"\n ? root.type\n : root.event;\n return typeof candidate === \"string\" ? candidate : \"unknown\";\n}\n\n/** Provider webhook id (`wbk_*` on FLW v4, sometimes on v3 payloads; `evt_*` on Stripe). */\nfunction extractWebhookId(root: Record<string, unknown>): string | undefined {\n return typeof root.id === \"string\" && root.id.length > 0 ? root.id : undefined;\n}\n\n/**\n * Idempotency key:\n * - Flutterwave: the webhook `id` when present, else `data.id + ':' + status`.\n * - Paystack (no webhook id): `sha256(event + ':' + data.id + ':' + data.status)`.\n * - Stripe: the globally-unique event `id` (`evt_*`); Paystack-style hash as a\n * defensive fallback (Stripe always sends one).\n */\nfunction computeDedupeKey(\n provider: WebhookProvider,\n nativeType: string,\n data: Record<string, unknown> | undefined,\n webhookId: string | undefined,\n): string {\n // Fall back through common per-resource identifiers: many non-transaction\n // events (transfers, subscriptions, customers) carry no `data.id` but do\n // carry a unique code. Without this the key collapses to `type:status` and\n // distinct webhooks collide and get wrongly deduped.\n const idCandidate =\n data?.id ??\n data?.reference ??\n data?.transfer_code ??\n data?.subscription_code ??\n data?.customer_code;\n const dataId =\n idCandidate !== undefined && idCandidate !== null ? String(idCandidate) : \"\";\n const status = typeof data?.status === \"string\" ? data.status : \"\";\n if (provider === \"flutterwave\") {\n if (webhookId !== undefined) return webhookId;\n return `${dataId}:${status}`;\n }\n if (provider === \"stripe\" && webhookId !== undefined) return webhookId;\n return createHash(\"sha256\").update(`${nativeType}:${dataId}:${status}`).digest(\"hex\");\n}\n\n/**\n * Verify (via the facade's dispatch) → `JSON.parse` the raw bytes → normalize.\n * Fails closed: a bad signature throws {@link PayweaveWebhookVerificationError};\n * malformed JSON on an otherwise-verified body likewise throws. Unmapped events\n * are still returned with `unifiedType: \"unknown\"` and the native `type`\n * preserved — never dropped.\n *\n * Verification runs on the EXACT bytes and the body is NEVER re-serialized: the\n * returned `data`/`raw` come straight from `JSON.parse(rawBody)`.\n */\nexport function constructEvent(params: ConstructEventParams): WebhookEvent {\n const { rawBody, headers, provider, version, verify, logger, billing } = params;\n\n // Reuse the facade's verify dispatch (may throw PayweaveConfigError for FLW\n // without a webhookSecret — propagate that, fail closed).\n if (!verify({ rawBody, headers })) {\n throw new PayweaveWebhookVerificationError(\"Webhook signature verification failed.\", {\n provider,\n isRetryable: false,\n });\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(decodeBody(rawBody));\n } catch (cause) {\n throw new PayweaveWebhookVerificationError(\"Webhook body is not valid JSON.\", {\n provider,\n isRetryable: false,\n cause,\n });\n }\n\n const root = asRecord(parsed) ?? {};\n const data = root.data;\n const nativeType = extractNativeType(root, provider);\n // Every provider — Stripe included — normalizes through the SAME\n // `unified/mappings.ts` tables. Stripe's own event-name rows (flat\n // `STRIPE_EVENT_MAP` + nested-resource `STRIPE_EVENT_STATUS_SPLIT_MAP`)\n // live inside `toUnifiedEventType` itself; an unmapped\n // Stripe event falls through to `\"unknown\"` exactly like an unmapped\n // Paystack/Flutterwave event — never a special case here.\n const unifiedType = toUnifiedEventType(provider, version, nativeType, data);\n const dataRecord = asRecord(data);\n // Normalize the transaction status for its drift side effect: an unrecognized\n // status emits a `schema_drift` log (never throws) so table gaps surface.\n // Stripe is excluded here (not from unifiedType mapping above) because its\n // event envelope nests the resource one level down (`data.object.<field>`,\n // e.g. Checkout Session `payment_status` or PaymentIntent `status`) rather\n // than the flat `data.status` Paystack/Flutterwave carry — reading\n // `dataRecord?.status` for Stripe would just be `undefined` every time and\n // emit spurious drift logs. `toUnifiedEventType`'s own Stripe branch already\n // reads the correct nested field per event via `STRIPE_EVENT_STATUS_SPLIT_MAP`.\n if (provider !== \"stripe\" && typeof dataRecord?.status === \"string\") {\n toUnifiedStatus(provider, version, dataRecord.status, logger);\n }\n const webhookId =\n provider === \"flutterwave\" || provider === \"stripe\" ? extractWebhookId(root) : undefined;\n const dedupeKey = computeDedupeKey(provider, nativeType, dataRecord, webhookId);\n\n // Built WITHOUT `apply` first: `apply`'s closure needs to reference the\n // finished event (`dedupeKey`/`type`/`unifiedType`/`data`/`provider`), so it\n // is attached in a second step rather than inline in this literal.\n const base: Omit<WebhookEvent, \"apply\"> = {\n provider,\n type: nativeType,\n unifiedType,\n data,\n raw: parsed,\n dedupeKey,\n ...(webhookId !== undefined ? { id: webhookId } : {}),\n };\n\n return {\n ...base,\n // always present (see the `WebhookEvent.apply` doc comment);\n // `billing` is `undefined` for a database-less client, in which case\n // `applyWebhookEvent` itself throws `PayweaveConfigError` when called.\n apply: (options) => applyWebhookEvent(billing ?? NO_DATABASE_CONTEXT, base, { logger, ...options }),\n };\n}\n"]}
@@ -0,0 +1,18 @@
1
+ import { timingSafeEqual, createHmac } from 'crypto';
2
+
3
+ // src/webhooks/flutterwave.ts
4
+ function verifyFlutterwaveV3(verifHashHeader, webhookSecret) {
5
+ const received = Buffer.from(verifHashHeader ?? "", "utf8");
6
+ const expected = Buffer.from(webhookSecret, "utf8");
7
+ return expected.length === received.length && timingSafeEqual(expected, received);
8
+ }
9
+ function verifyFlutterwaveV4(rawBody, signatureHeader, webhookSecret) {
10
+ const digest = createHmac("sha256", webhookSecret).update(rawBody).digest("base64");
11
+ const expected = Buffer.from(digest, "utf8");
12
+ const received = Buffer.from(signatureHeader ?? "", "utf8");
13
+ return expected.length === received.length && timingSafeEqual(expected, received);
14
+ }
15
+
16
+ export { verifyFlutterwaveV3, verifyFlutterwaveV4 };
17
+ //# sourceMappingURL=chunk-CJEYDHY6.js.map
18
+ //# sourceMappingURL=chunk-CJEYDHY6.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/webhooks/flutterwave.ts","../src/webhooks/flutterwave-v4.ts"],"names":["timingSafeEqual"],"mappings":";;;AAeO,SAAS,mBAAA,CACd,iBACA,aAAA,EACS;AACT,EAAA,MAAM,QAAA,GAAW,MAAA,CAAO,IAAA,CAAK,eAAA,IAAmB,IAAI,MAAM,CAAA;AAC1D,EAAA,MAAM,QAAA,GAAW,MAAA,CAAO,IAAA,CAAK,aAAA,EAAe,MAAM,CAAA;AAClD,EAAA,OAAO,SAAS,MAAA,KAAW,QAAA,CAAS,MAAA,IAAU,eAAA,CAAgB,UAAU,QAAQ,CAAA;AAClF;ACNO,SAAS,mBAAA,CACd,OAAA,EACA,eAAA,EACA,aAAA,EACS;AACT,EAAA,MAAM,MAAA,GAAS,WAAW,QAAA,EAAU,aAAa,EAAE,MAAA,CAAO,OAAO,CAAA,CAAE,MAAA,CAAO,QAAQ,CAAA;AAClF,EAAA,MAAM,QAAA,GAAW,MAAA,CAAO,IAAA,CAAK,MAAA,EAAQ,MAAM,CAAA;AAC3C,EAAA,MAAM,QAAA,GAAW,MAAA,CAAO,IAAA,CAAK,eAAA,IAAmB,IAAI,MAAM,CAAA;AAC1D,EAAA,OAAO,SAAS,MAAA,KAAW,QAAA,CAAS,MAAA,IAAUA,eAAAA,CAAgB,UAAU,QAAQ,CAAA;AAClF","file":"chunk-CJEYDHY6.js","sourcesContent":["/**\n * Flutterwave v3 webhook verification. v3 uses NO HMAC: the\n * `verif-hash` header must equal the dashboard secret hash verbatim. Compared\n * timing-safely with a length-guard; fails closed on a missing header.\n *\n * Docs: https://developer.flutterwave.com/docs/webhooks (v3 selector)\n * Note: the FLW webhook secret is the dashboard \"secret hash\", NOT the API key.\n */\nimport { timingSafeEqual } from \"node:crypto\";\n\n/**\n * @param verifHashHeader - Value of the `verif-hash` header.\n * @param webhookSecret - Dashboard secret hash configured for webhooks.\n * @returns `true` iff the header equals the secret hash; `false` otherwise.\n */\nexport function verifyFlutterwaveV3(\n verifHashHeader: string | null | undefined,\n webhookSecret: string,\n): boolean {\n const received = Buffer.from(verifHashHeader ?? \"\", \"utf8\");\n const expected = Buffer.from(webhookSecret, \"utf8\");\n return expected.length === received.length && timingSafeEqual(expected, received);\n}\n","/**\n * Flutterwave v4 webhook verification. HMAC-SHA256 over the RAW body,\n * BASE64 digest, keyed with the dashboard secret hash. Timing-safe with a\n * length-guard; fails closed on a missing header. Active only when the client's\n * configured `version` is `\"v4\"` — never accept both v3 and v4 on one client.\n *\n * Docs: https://developer.flutterwave.com/docs/webhooks (v4 selector) ⚠️ verify\n */\nimport { createHmac, timingSafeEqual } from \"node:crypto\";\n\n/**\n * @param rawBody - Exact received bytes (never a re-serialized object).\n * @param signatureHeader - Value of the `flutterwave-signature` header.\n * @param webhookSecret - Dashboard secret hash (HMAC-SHA256 key).\n * @returns `true` iff the signature matches; `false` otherwise (never throws).\n */\nexport function verifyFlutterwaveV4(\n rawBody: string | Uint8Array,\n signatureHeader: string | null | undefined,\n webhookSecret: string,\n): boolean {\n const digest = createHmac(\"sha256\", webhookSecret).update(rawBody).digest(\"base64\");\n const expected = Buffer.from(digest, \"utf8\");\n const received = Buffer.from(signatureHeader ?? \"\", \"utf8\");\n return expected.length === received.length && timingSafeEqual(expected, received);\n}\n"]}
@@ -0,0 +1,3 @@
1
+
2
+ //# sourceMappingURL=chunk-CR63JAAS.js.map
3
+ //# sourceMappingURL=chunk-CR63JAAS.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"chunk-CR63JAAS.js"}