@monetize.software/sdk 3.0.0-alpha.9 → 3.0.0-beta.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 (212) hide show
  1. package/dist/chunks/PaywallUI-CCwYgagL.js +26 -0
  2. package/dist/chunks/PaywallUI-CCwYgagL.js.map +1 -0
  3. package/dist/chunks/PaywallUI-CDlJL5Em.js +3599 -0
  4. package/dist/chunks/PaywallUI-CDlJL5Em.js.map +1 -0
  5. package/dist/chunks/ar-CViBaj16.js +2 -0
  6. package/dist/chunks/ar-CViBaj16.js.map +1 -0
  7. package/dist/chunks/{ar-B2Wg_IrC.js → ar-ClwBmll7.js} +5 -1
  8. package/dist/chunks/ar-ClwBmll7.js.map +1 -0
  9. package/dist/chunks/{cs-BNo9Dx0Q.js → cs-Cc_D0W4S.js} +5 -1
  10. package/dist/chunks/cs-Cc_D0W4S.js.map +1 -0
  11. package/dist/chunks/cs-DQJWP65K.js +2 -0
  12. package/dist/chunks/cs-DQJWP65K.js.map +1 -0
  13. package/dist/chunks/{da-Do9Lq6En.js → da-BtKcMSxH.js} +5 -1
  14. package/dist/chunks/da-BtKcMSxH.js.map +1 -0
  15. package/dist/chunks/da-Cyr_IXhb.js +2 -0
  16. package/dist/chunks/da-Cyr_IXhb.js.map +1 -0
  17. package/dist/chunks/de-BOCUj1pF.js +2 -0
  18. package/dist/chunks/de-BOCUj1pF.js.map +1 -0
  19. package/dist/chunks/{de-C8pDZNvx.js → de-iwQW5eYl.js} +5 -1
  20. package/dist/chunks/de-iwQW5eYl.js.map +1 -0
  21. package/dist/chunks/{el-DzMNX-_P.js → el-BoH65CeJ.js} +5 -1
  22. package/dist/chunks/el-BoH65CeJ.js.map +1 -0
  23. package/dist/chunks/el-C6jLHiEF.js +2 -0
  24. package/dist/chunks/el-C6jLHiEF.js.map +1 -0
  25. package/dist/chunks/es-CARHLxHR.js +2 -0
  26. package/dist/chunks/es-CARHLxHR.js.map +1 -0
  27. package/dist/chunks/{es-YrKt-q4w.js → es-CkP16_8K.js} +5 -1
  28. package/dist/chunks/es-CkP16_8K.js.map +1 -0
  29. package/dist/chunks/fi-B8OhRbwY.js +2 -0
  30. package/dist/chunks/fi-B8OhRbwY.js.map +1 -0
  31. package/dist/chunks/{fi-Bh44pwZ4.js → fi-yUXSmK1E.js} +5 -1
  32. package/dist/chunks/fi-yUXSmK1E.js.map +1 -0
  33. package/dist/chunks/{fr-BhYf-iKk.js → fr-CoM18zzM.js} +2 -2
  34. package/dist/chunks/fr-CoM18zzM.js.map +1 -0
  35. package/dist/chunks/{fr-Bc0pw4ws.js → fr-DSZRGsxI.js} +5 -1
  36. package/dist/chunks/fr-DSZRGsxI.js.map +1 -0
  37. package/dist/chunks/he-DTDVc3lA.js +2 -0
  38. package/dist/chunks/he-DTDVc3lA.js.map +1 -0
  39. package/dist/chunks/{he-Bfm-bhe3.js → he-ZUo7cQjk.js} +7 -3
  40. package/dist/chunks/he-ZUo7cQjk.js.map +1 -0
  41. package/dist/chunks/hi-BjnS9UeE.js +2 -0
  42. package/dist/chunks/hi-BjnS9UeE.js.map +1 -0
  43. package/dist/chunks/{hi-D-O-B9Dn.js → hi-CEutBKiG.js} +5 -1
  44. package/dist/chunks/hi-CEutBKiG.js.map +1 -0
  45. package/dist/chunks/hu-CN06FMhV.js +2 -0
  46. package/dist/chunks/hu-CN06FMhV.js.map +1 -0
  47. package/dist/chunks/{hu-CmIuAbLL.js → hu-DvEAWGS7.js} +5 -1
  48. package/dist/chunks/hu-DvEAWGS7.js.map +1 -0
  49. package/dist/chunks/id-BITgaBxq.js +2 -0
  50. package/dist/chunks/id-BITgaBxq.js.map +1 -0
  51. package/dist/chunks/{id-DN7IES-A.js → id-_eEMrYMW.js} +5 -1
  52. package/dist/chunks/id-_eEMrYMW.js.map +1 -0
  53. package/dist/chunks/index-CLB1AgLg.js +2074 -0
  54. package/dist/chunks/index-CLB1AgLg.js.map +1 -0
  55. package/dist/chunks/index-CzDSBl4d.js +2 -0
  56. package/dist/chunks/index-CzDSBl4d.js.map +1 -0
  57. package/dist/chunks/{it-Cz5Nmqx5.js → it-BAmsQbIl.js} +5 -1
  58. package/dist/chunks/it-BAmsQbIl.js.map +1 -0
  59. package/dist/chunks/it-DVcsq015.js +2 -0
  60. package/dist/chunks/it-DVcsq015.js.map +1 -0
  61. package/dist/chunks/ja-Bim7Ok2e.js +2 -0
  62. package/dist/chunks/ja-Bim7Ok2e.js.map +1 -0
  63. package/dist/chunks/{ja-BH9BlBh2.js → ja-BqCZWVKK.js} +7 -3
  64. package/dist/chunks/ja-BqCZWVKK.js.map +1 -0
  65. package/dist/chunks/{ko-CYV9QuYs.js → ko-CkIzeOzb.js} +7 -3
  66. package/dist/chunks/ko-CkIzeOzb.js.map +1 -0
  67. package/dist/chunks/ko-DqEfcVCB.js +2 -0
  68. package/dist/chunks/ko-DqEfcVCB.js.map +1 -0
  69. package/dist/chunks/nl-DSNuJWIo.js +2 -0
  70. package/dist/chunks/nl-DSNuJWIo.js.map +1 -0
  71. package/dist/chunks/{nl-BvkB900D.js → nl-Mu_KNzli.js} +5 -1
  72. package/dist/chunks/nl-Mu_KNzli.js.map +1 -0
  73. package/dist/chunks/{no-3s9_ormb.js → no-9HMjljBs.js} +5 -1
  74. package/dist/chunks/no-9HMjljBs.js.map +1 -0
  75. package/dist/chunks/no-B5HY_A8_.js +2 -0
  76. package/dist/chunks/no-B5HY_A8_.js.map +1 -0
  77. package/dist/chunks/{pl-C9WTGQtb.js → pl-Cbr2qDwv.js} +5 -1
  78. package/dist/chunks/pl-Cbr2qDwv.js.map +1 -0
  79. package/dist/chunks/pl-ChGLn9Sy.js +2 -0
  80. package/dist/chunks/pl-ChGLn9Sy.js.map +1 -0
  81. package/dist/chunks/pt-CDLEktT9.js +2 -0
  82. package/dist/chunks/pt-CDLEktT9.js.map +1 -0
  83. package/dist/chunks/{pt-uFVUv_Op.js → pt-_jirA6IR.js} +5 -1
  84. package/dist/chunks/pt-_jirA6IR.js.map +1 -0
  85. package/dist/chunks/ro-BHCHiXY3.js +2 -0
  86. package/dist/chunks/ro-BHCHiXY3.js.map +1 -0
  87. package/dist/chunks/{ro-BrqQ8Au-.js → ro-oZLxhwHc.js} +5 -1
  88. package/dist/chunks/ro-oZLxhwHc.js.map +1 -0
  89. package/dist/chunks/ru-B6HgmINj.js +2 -0
  90. package/dist/chunks/ru-B6HgmINj.js.map +1 -0
  91. package/dist/chunks/{ru-DK594dA8.js → ru-C2-sqY7B.js} +5 -1
  92. package/dist/chunks/ru-C2-sqY7B.js.map +1 -0
  93. package/dist/chunks/sv-DoSs4a9n.js +2 -0
  94. package/dist/chunks/sv-DoSs4a9n.js.map +1 -0
  95. package/dist/chunks/{sv-CHNH8-mq.js → sv-y385MWVy.js} +5 -1
  96. package/dist/chunks/sv-y385MWVy.js.map +1 -0
  97. package/dist/chunks/{th-l24Pm5q-.js → th-Bjbv4M1s.js} +5 -1
  98. package/dist/chunks/th-Bjbv4M1s.js.map +1 -0
  99. package/dist/chunks/th-UeweOhwg.js +2 -0
  100. package/dist/chunks/th-UeweOhwg.js.map +1 -0
  101. package/dist/chunks/tr-BbBr5tyB.js +2 -0
  102. package/dist/chunks/tr-BbBr5tyB.js.map +1 -0
  103. package/dist/chunks/{tr-ADpigSY5.js → tr-DmHo9F8i.js} +7 -3
  104. package/dist/chunks/tr-DmHo9F8i.js.map +1 -0
  105. package/dist/chunks/uk-CJTA1SJK.js +2 -0
  106. package/dist/chunks/uk-CJTA1SJK.js.map +1 -0
  107. package/dist/chunks/{uk-CGqo4jek.js → uk-DwvAoTe5.js} +5 -1
  108. package/dist/chunks/uk-DwvAoTe5.js.map +1 -0
  109. package/dist/chunks/{vi-Dk9bTu6f.js → vi-BEpwsVLR.js} +5 -1
  110. package/dist/chunks/vi-BEpwsVLR.js.map +1 -0
  111. package/dist/chunks/vi-ZvBo7PbB.js +2 -0
  112. package/dist/chunks/vi-ZvBo7PbB.js.map +1 -0
  113. package/dist/chunks/zh-CPZBEb3d.js +2 -0
  114. package/dist/chunks/zh-CPZBEb3d.js.map +1 -0
  115. package/dist/chunks/{zh-LDkEV2D9.js → zh-DrG18oGe.js} +7 -3
  116. package/dist/chunks/zh-DrG18oGe.js.map +1 -0
  117. package/dist/core.cjs +1 -1
  118. package/dist/core.cjs.map +1 -1
  119. package/dist/core.d.ts +89 -4
  120. package/dist/core.js +17 -2004
  121. package/dist/core.js.map +1 -1
  122. package/dist/index.cjs +1 -1
  123. package/dist/index.d.ts +188 -5
  124. package/dist/index.js +17 -13
  125. package/dist/ui.cjs +1 -1
  126. package/dist/ui.d.ts +153 -4
  127. package/dist/ui.js +1 -1
  128. package/package.json +1 -1
  129. package/dist/chunks/PaywallUI-BhQpWSWN.js +0 -3224
  130. package/dist/chunks/PaywallUI-BhQpWSWN.js.map +0 -1
  131. package/dist/chunks/PaywallUI-C7Wp2TiR.js +0 -26
  132. package/dist/chunks/PaywallUI-C7Wp2TiR.js.map +0 -1
  133. package/dist/chunks/ar-7cgIM-Vl.js +0 -2
  134. package/dist/chunks/ar-7cgIM-Vl.js.map +0 -1
  135. package/dist/chunks/ar-B2Wg_IrC.js.map +0 -1
  136. package/dist/chunks/cs-BNo9Dx0Q.js.map +0 -1
  137. package/dist/chunks/cs-S05PC5AC.js +0 -2
  138. package/dist/chunks/cs-S05PC5AC.js.map +0 -1
  139. package/dist/chunks/da-Bi4zBG14.js +0 -2
  140. package/dist/chunks/da-Bi4zBG14.js.map +0 -1
  141. package/dist/chunks/da-Do9Lq6En.js.map +0 -1
  142. package/dist/chunks/de-C8pDZNvx.js.map +0 -1
  143. package/dist/chunks/de-nCDB6D2W.js +0 -2
  144. package/dist/chunks/de-nCDB6D2W.js.map +0 -1
  145. package/dist/chunks/el-BrKaa978.js +0 -2
  146. package/dist/chunks/el-BrKaa978.js.map +0 -1
  147. package/dist/chunks/el-DzMNX-_P.js.map +0 -1
  148. package/dist/chunks/es-B-Wtyzrl.js +0 -2
  149. package/dist/chunks/es-B-Wtyzrl.js.map +0 -1
  150. package/dist/chunks/es-YrKt-q4w.js.map +0 -1
  151. package/dist/chunks/fi-Bh44pwZ4.js.map +0 -1
  152. package/dist/chunks/fi-D1SGXjnO.js +0 -2
  153. package/dist/chunks/fi-D1SGXjnO.js.map +0 -1
  154. package/dist/chunks/fr-Bc0pw4ws.js.map +0 -1
  155. package/dist/chunks/fr-BhYf-iKk.js.map +0 -1
  156. package/dist/chunks/he-BXAaFv6Y.js +0 -2
  157. package/dist/chunks/he-BXAaFv6Y.js.map +0 -1
  158. package/dist/chunks/he-Bfm-bhe3.js.map +0 -1
  159. package/dist/chunks/hi-D-O-B9Dn.js.map +0 -1
  160. package/dist/chunks/hi-xblDO0O7.js +0 -2
  161. package/dist/chunks/hi-xblDO0O7.js.map +0 -1
  162. package/dist/chunks/hu-CmIuAbLL.js.map +0 -1
  163. package/dist/chunks/hu-Wa46p0y4.js +0 -2
  164. package/dist/chunks/hu-Wa46p0y4.js.map +0 -1
  165. package/dist/chunks/id-CQEo5X94.js +0 -2
  166. package/dist/chunks/id-CQEo5X94.js.map +0 -1
  167. package/dist/chunks/id-DN7IES-A.js.map +0 -1
  168. package/dist/chunks/it-8AYCm0xz.js +0 -2
  169. package/dist/chunks/it-8AYCm0xz.js.map +0 -1
  170. package/dist/chunks/it-Cz5Nmqx5.js.map +0 -1
  171. package/dist/chunks/ja-BH9BlBh2.js.map +0 -1
  172. package/dist/chunks/ja-q-COVayn.js +0 -2
  173. package/dist/chunks/ja-q-COVayn.js.map +0 -1
  174. package/dist/chunks/ko-B6HRCscZ.js +0 -2
  175. package/dist/chunks/ko-B6HRCscZ.js.map +0 -1
  176. package/dist/chunks/ko-CYV9QuYs.js.map +0 -1
  177. package/dist/chunks/nl-BvkB900D.js.map +0 -1
  178. package/dist/chunks/nl-CAd6_xlm.js +0 -2
  179. package/dist/chunks/nl-CAd6_xlm.js.map +0 -1
  180. package/dist/chunks/no-3s9_ormb.js.map +0 -1
  181. package/dist/chunks/no-CAmz6bz6.js +0 -2
  182. package/dist/chunks/no-CAmz6bz6.js.map +0 -1
  183. package/dist/chunks/pl-C9WTGQtb.js.map +0 -1
  184. package/dist/chunks/pl-DqUSTCaF.js +0 -2
  185. package/dist/chunks/pl-DqUSTCaF.js.map +0 -1
  186. package/dist/chunks/pt-8ARZnH0_.js +0 -2
  187. package/dist/chunks/pt-8ARZnH0_.js.map +0 -1
  188. package/dist/chunks/pt-uFVUv_Op.js.map +0 -1
  189. package/dist/chunks/ro-BrqQ8Au-.js.map +0 -1
  190. package/dist/chunks/ro-D-NMbp2F.js +0 -2
  191. package/dist/chunks/ro-D-NMbp2F.js.map +0 -1
  192. package/dist/chunks/ru-8gbHPh0g.js +0 -2
  193. package/dist/chunks/ru-8gbHPh0g.js.map +0 -1
  194. package/dist/chunks/ru-DK594dA8.js.map +0 -1
  195. package/dist/chunks/sv-CHNH8-mq.js.map +0 -1
  196. package/dist/chunks/sv-D8a8hmx9.js +0 -2
  197. package/dist/chunks/sv-D8a8hmx9.js.map +0 -1
  198. package/dist/chunks/th-DfjUK0Y7.js +0 -2
  199. package/dist/chunks/th-DfjUK0Y7.js.map +0 -1
  200. package/dist/chunks/th-l24Pm5q-.js.map +0 -1
  201. package/dist/chunks/tr-ADpigSY5.js.map +0 -1
  202. package/dist/chunks/tr-BdBpz4tL.js +0 -2
  203. package/dist/chunks/tr-BdBpz4tL.js.map +0 -1
  204. package/dist/chunks/uk-CGqo4jek.js.map +0 -1
  205. package/dist/chunks/uk-Cx1zv1ao.js +0 -2
  206. package/dist/chunks/uk-Cx1zv1ao.js.map +0 -1
  207. package/dist/chunks/vi-Dk9bTu6f.js.map +0 -1
  208. package/dist/chunks/vi-oe2dW21I.js +0 -2
  209. package/dist/chunks/vi-oe2dW21I.js.map +0 -1
  210. package/dist/chunks/zh-CwczPMPp.js +0 -2
  211. package/dist/chunks/zh-CwczPMPp.js.map +0 -1
  212. package/dist/chunks/zh-LDkEV2D9.js.map +0 -1
package/dist/index.cjs CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./core.cjs"),r=require("./chunks/PaywallUI-C7Wp2TiR.js");exports.ApiClient=e.ApiClient;exports.ApiGatewayClient=e.ApiGatewayClient;exports.AuthClient=e.AuthClient;exports.BillingClient=e.BillingClient;exports.EventTracker=e.EventTracker;exports.PaywallError=e.PaywallError;exports.QuotaExceededError=e.QuotaExceededError;exports.SDK_VERSION=e.SDK_VERSION;exports.STORAGE_KEYS=e.STORAGE_KEYS;exports.createStorage=e.createStorage;exports.ensureVisitorId=e.ensureVisitorId;exports.generateVisitorId=e.generateVisitorId;exports.PaywallUI=r.PaywallUI;exports.blockRegistry=r.blockRegistry;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./chunks/index-CzDSBl4d.js"),r=require("./chunks/PaywallUI-CCwYgagL.js");exports.ApiClient=e.ApiClient;exports.ApiGatewayClient=e.ApiGatewayClient;exports.AuthClient=e.AuthClient;exports.BillingClient=e.BillingClient;exports.EventTracker=e.EventTracker;exports.PaywallError=e.PaywallError;exports.QuotaExceededError=e.QuotaExceededError;exports.SDK_VERSION=e.SDK_VERSION;exports.STORAGE_KEYS=e.STORAGE_KEYS;exports.createStorage=e.createStorage;exports.ensureVisitorId=e.ensureVisitorId;exports.findApplicableOffer=e.findApplicableOffer;exports.generateVisitorId=e.generateVisitorId;exports.offerStartStorageKey=e.offerStartStorageKey;exports.readBrowserOfferStart=e.readBrowserOfferStart;exports.resolveOffer=e.resolveOffer;exports.PaywallUI=r.PaywallUI;exports.blockRegistry=r.blockRegistry;
2
2
  //# sourceMappingURL=index.cjs.map
package/dist/index.d.ts CHANGED
@@ -637,6 +637,13 @@ export declare class BillingClient {
637
637
  }): Promise<PaywallPrice[]>;
638
638
  /** Sync-снимок цен из последнего bootstrap'а. null = ещё не загружали. */
639
639
  getCachedPrices(): PaywallPrice[] | null;
640
+ /** Sync-снимок офферов из последнего bootstrap'а. null = bootstrap ещё не
641
+ * загружали, пустой массив = бэк отдал пейвол без офферов. Бэк уже
642
+ * применил серверный таргетинг (target_countries / target_emails /
643
+ * targeting_mode из offer_settings) — наружу выезжает только то, что
644
+ * применимо к текущему юзеру. Клиентская сторона остаётся ответственной
645
+ * за price_id-matching и countdown (см. core/offer.ts → resolveOffer). */
646
+ getCachedOffers(): PaywallOffer[] | null;
640
647
  /**
641
648
  * Снимок того, какой язык SDK сейчас считает «языком юзера». Полезно для
642
649
  * синхронизации i18n хоста с тем, что фактически показывает пейвол — чтобы
@@ -763,6 +770,17 @@ export declare class BillingClient {
763
770
  errorUrl?: string;
764
771
  shopUrl?: string;
765
772
  trialDays?: number;
773
+ /** Активный offer для этой цены — резолвится host'ом через
774
+ * `paywall.getOfferForPrice(priceId)?.offer.id` или
775
+ * `findApplicableOffer(client.getCachedOffers(), priceId)?.id`. Без
776
+ * явной передачи бэк сделает auto-resolve по email — но только для
777
+ * end_date-офферов. duration_minutes-офферы тикают в clientStorage и
778
+ * сервер их не видит: для них offerId ОБЯЗАН прийти от клиента, иначе
779
+ * скидка не применится на чекауте, хотя UI её показывал.
780
+ *
781
+ * Передавать offer-id всегда безопасно — бэк сам проверит applicable
782
+ * ли offer к этому юзеру (страна/email/режим) и игнорирует если нет. */
783
+ offerId?: string;
766
784
  /**
767
785
  * Stage 1 защиты от дубликатов покупок. Идемпотентный ключ запроса
768
786
  * (UUID). Повторный вызов с тем же ключом вернёт тот же checkout-URL
@@ -789,7 +807,9 @@ export declare class BillingClient {
789
807
  * инвойсы). Опен-флоу управляется host'ом:
790
808
  *
791
809
  * ```ts
792
- * const { url } = await billing.getCustomerPortalUrl();
810
+ * const { url } = await billing.getCustomerPortalUrl({
811
+ * returnUrl: 'https://your-app.com/account'
812
+ * });
793
813
  * window.open(url, '_blank');
794
814
  * ```
795
815
  *
@@ -801,6 +821,14 @@ export declare class BillingClient {
801
821
  */
802
822
  getCustomerPortalUrl(opts?: {
803
823
  signal?: AbortSignal;
824
+ /** URL для return-button у провайдера (Stripe «Return to ...», Paddle
825
+ * и Chargebee redirect_url'ы). Передавай туда страницу-аккаунт твоего
826
+ * app'а — `https://your-app.com/account`. Без явного returnUrl бэк
827
+ * применяет fallback в порядке: `paywall_settings.shop_url` →
828
+ * custom_domain пейвола → NEXT_PUBLIC_ONLINE_ORIGIN (последнее — это
829
+ * страница в самом online-сервисе, годится только для legacy
830
+ * v2-iframe-флоу). */
831
+ returnUrl?: string;
804
832
  }): Promise<{
805
833
  url: string;
806
834
  }>;
@@ -992,6 +1020,11 @@ export declare interface EventTrackerOptions {
992
1020
  sendBeacon?: (url: string, data: BodyInit) => boolean;
993
1021
  }
994
1022
 
1023
+ /** Pick the offer applicable to a price. Targeted (`price_id === id`) wins
1024
+ * over the global default (`price_id === null`). Offers without a positive
1025
+ * `discount_percent` are ignored. */
1026
+ export declare function findApplicableOffer(offers: PaywallOffer[] | null | undefined, priceId: string): PaywallOffer | null;
1027
+
995
1028
  export declare function generateVisitorId(): string;
996
1029
 
997
1030
  export declare interface GetAccessOptions {
@@ -1158,6 +1191,11 @@ export declare interface LocaleOverrides {
1158
1191
 
1159
1192
  export declare type OAuthProvider = 'google' | 'apple' | 'github' | 'facebook';
1160
1193
 
1194
+ /** Storage key under which a relative `duration_minutes` offer records its
1195
+ * first-view timestamp. Shared between the renderer (which writes the
1196
+ * start on first open) and the host SDK helpers (which read it). */
1197
+ export declare function offerStartStorageKey(offerId: string): string;
1198
+
1161
1199
  export declare interface OpenOptions {
1162
1200
  identity?: Identity;
1163
1201
  /** Принудительно открыть, минуя pre-paywall trial check. По умолчанию SDK
@@ -1335,8 +1373,10 @@ export declare interface PaywallOffer {
1335
1373
  expires_at: string | null;
1336
1374
  /** Относительный таймер: сколько минут offer живёт **от первого просмотра
1337
1375
  * пейвола** данным юзером. Старт хранится в clientStorage под ключом
1338
- * `pw-offer-{id}-start`, по истечении auto-cleanup. Используется, когда
1339
- * бэк хочет показывать "remaining time" без жёсткой server-time, а считать
1376
+ * `pw-offer-{id}-start` и остаётся там **после** expiry — это
1377
+ * forever-marker, чтобы юзер не мог «фармить» offer (re-open пейвола
1378
+ * не запускает countdown заново). Используется, когда бэк хочет
1379
+ * показывать "remaining time" без жёсткой server-time, а считать
1340
1380
  * относительно сессии юзера. expires_at имеет приоритет если задано. */
1341
1381
  duration_minutes?: number | null;
1342
1382
  price_id: string | null;
@@ -1363,7 +1403,7 @@ export declare interface PaywallPrice {
1363
1403
  * идёт из `/user-state` и имеет минимум для access-gate'а), этот shape
1364
1404
  * включает цену/валюту/discount — чтобы host мог нарисовать список подписок
1365
1405
  * как в legacy customer portal'е. */
1366
- declare interface PaywallPurchaseDetailed {
1406
+ export declare interface PaywallPurchaseDetailed {
1367
1407
  id: string;
1368
1408
  status: string | null;
1369
1409
  cancel_at: string | null;
@@ -1452,12 +1492,25 @@ declare type PaywallStateListener = (state: PaywallStateSnapshot) => void;
1452
1492
  * один call onState; повторы (`useSyncExternalStore`-friendly).
1453
1493
  */
1454
1494
  export declare interface PaywallStateSnapshot {
1455
- /** Модалка отрендерена и видна. False — closed (или ещё не открывалась). */
1495
+ /** Модалка отрендерена и видна. False — closed (или ещё не открывалась).
1496
+ * Может быть false при `processing=true` — direct-checkout (paywall.checkout)
1497
+ * делает bootstrap + createCheckout headless до того, как решит надо ли
1498
+ * монтировать модалку. */
1456
1499
  open: boolean;
1457
1500
  /** Что показывается в модалке. null когда `open=false`. */
1458
1501
  view: 'loading' | 'error' | 'layout' | 'auth' | 'support' | 'awaiting_payment' | 'popup_blocked' | 'purchased' | null;
1459
1502
  /** Заполнено только когда `view === 'error'`. */
1460
1503
  error: PaywallError | null;
1504
+ /** SDK делает фоновую работу для `paywall.checkout(priceId)` — bootstrap,
1505
+ * visibility/trial-gates, createCheckout — до того, как реально нужна
1506
+ * UI-модалка. Хост на этой фазе может задизейблить свою кнопку и
1507
+ * показать спиннер прямо на ней, чтобы у юзера не было ощущения «клик
1508
+ * ничего не сделал». Сбрасывается в false сразу после mountAndShow
1509
+ * (модалка взяла на себя UI), либо после headless reject (already-paid,
1510
+ * ошибка createCheckout без модалки). Для `paywall.open()`-flow всегда
1511
+ * false: там модалка появляется мгновенно со своим LoadingView и
1512
+ * отдельный «processing» не нужен. */
1513
+ processing: boolean;
1461
1514
  }
1462
1515
 
1463
1516
  export declare class PaywallUI {
@@ -1477,6 +1530,12 @@ export declare class PaywallUI {
1477
1530
  private watcher;
1478
1531
  private tracker;
1479
1532
  private purchased;
1533
+ /** View, с которым последний раз монтировалась модалка. Гейтит аналитику
1534
+ * `paywall_opened`/`paywall_viewed` на реальный пейвол (`'layout'`): открытие
1535
+ * support / standalone-auth / awaiting_payment эмитит публичный `'open'` и
1536
+ * `'ready'`, но это не «пейвол открыт/просмотрен» — иначе саппорт-клик шлёт
1537
+ * ложный `paywall_opened`. */
1538
+ private lastMountedView;
1480
1539
  /** Lazy-инстанс TrialStore. Резолвится при первом open(), когда уже знаем
1481
1540
  * `bootstrap.settings.trial`. null — триал отключён в конфиге пейвола. */
1482
1541
  private trialStore;
@@ -1598,6 +1657,65 @@ export declare class PaywallUI {
1598
1657
  * Без managed-auth — no-op.
1599
1658
  */
1600
1659
  openSignup(opts?: OpenOptions): void;
1660
+ /**
1661
+ * Direct-checkout: создать checkout-URL по конкретной цене и сразу открыть
1662
+ * платёжного провайдера, минуя layout с тарифами. Полезно когда
1663
+ * host-приложение рендерит pricing-карточки/таблицу собственным UI и
1664
+ * хочет, чтобы клик по «Buy / Get this plan» вёл прямо в Stripe/Paddle.
1665
+ *
1666
+ * **Late-mount UX.** В отличие от `open()`, модалка не появляется во время
1667
+ * фоновой работы (bootstrap + visibility/trial gates + createCheckout).
1668
+ * Хост на этой фазе показывает busy-state прямо на своей кнопке (через
1669
+ * `state.processing === true` из `paywall.getState()` — или автоматически
1670
+ * через `<PaywallButton priceId>` в sdk-react). Модалка монтируется
1671
+ * ТОЛЬКО когда реально нужна UI:
1672
+ * - `checkout_mode='preauth'` + managed-auth + не залогинен → auth-gate
1673
+ * (форма signin'а); после успеха auto-resume в createCheckout.
1674
+ * - popup провайдера заблокирован браузером → popup_blocked view с
1675
+ * retry-кнопкой под fresh user gesture.
1676
+ * - popup открылся успешно → awaiting_payment view (индикатор «оплати
1677
+ * в новой вкладке» + I've paid).
1678
+ *
1679
+ * Что эмитится без модалки:
1680
+ * - `purchase_completed{restored:true, priceId}` когда юзер уже подписан
1681
+ * (cached user, fresh bootstrap, или 409 hasActivePurchase от бэка) —
1682
+ * headless reject;
1683
+ * - `error` когда createCheckout упал или identity.email отсутствует;
1684
+ * - `visibility_blocked` / `trial_blocked` — стандартные gate-эвенты.
1685
+ *
1686
+ * Что эмитится одновременно с модалкой:
1687
+ * - `checkout_started{priceId, url, acquiring}` ровно когда headless URL
1688
+ * получен, ДО mount'а awaiting_payment/popup_blocked.
1689
+ *
1690
+ * Offer (countdown-скидка) автоматически резолвится из cached offers'ов
1691
+ * через `getOfferForPrice(priceId)` и передаётся в createCheckout как
1692
+ * `offerId` — чтобы duration_minutes-офферы тоже применились на бэке
1693
+ * (для них нет server-side таймера, без явного offer-id скидка теряется).
1694
+ *
1695
+ * Требования:
1696
+ * - `identity.email` должен быть выставлен (через `opts.identity`, либо
1697
+ * managed-auth, либо ранний `setIdentity`/`paywall.open({identity})`).
1698
+ * Без email бэк `/start-checkout` 400'нёт; SDK эмитнет `error`.
1699
+ * - В `checkout_mode='preauth'` без managed-auth — backend требует
1700
+ * email-юзера; убедись что `identity.email` явно задан.
1701
+ *
1702
+ * Без модалки совсем (когда host рендерит свой awaiting-payment экран) —
1703
+ * используй `paywall.billing.createCheckout({priceId, offerId})` напрямую,
1704
+ * но тогда auth-gate / popup_blocked / awaiting_payment придётся рисовать
1705
+ * самостоятельно.
1706
+ */
1707
+ checkout(priceId: string, opts?: OpenOptions): void;
1708
+ /** Headless prep-work для `checkout(priceId, opts)`: bootstrap → gates →
1709
+ * preauth check → createCheckout → mount модалки с финальным view.
1710
+ * Вынесено отдельным методом ради чистого async/await flow вместо вложенных
1711
+ * then-chain'ов (5+ ветвей). Любая ошибка не пробрасывается наружу: эмитим
1712
+ * через `paywall.emit('error')` и выходим — host подписан на `error`-event. */
1713
+ private runDirectCheckout;
1714
+ /** Trial-check без mount'а (для late-mount direct-checkout). Возвращает
1715
+ * true если trial заблокировал — caller должен прекратить flow. На любой
1716
+ * storage-ошибке log+продолжаем (не блокируем продажу). */
1717
+ private checkTrialBeforeCheckout;
1718
+ private applyProcessing;
1601
1719
  /**
1602
1720
  * Headless anonymous signin без открытия модалки. Внутри:
1603
1721
  * idempotent (если уже анон — instant return) → resume через сохранённый
@@ -1664,6 +1782,26 @@ export declare class PaywallUI {
1664
1782
  }): Promise<PaywallPrice[]>;
1665
1783
  /** Sync-снимок цен. null — bootstrap ещё не загружали. */
1666
1784
  getCachedPrices(): PaywallPrice[] | null;
1785
+ /** Sync-снимок офферов. null = bootstrap не загружали, [] = пейвол без офферов.
1786
+ * Бэк уже применил серверный targeting (страны/email/режим) — наружу
1787
+ * выезжает только то, что применимо к текущему юзеру. */
1788
+ getCachedOffers(): PaywallOffer[] | null;
1789
+ /**
1790
+ * Резолвит активный offer для конкретной цены: price_id-таргетинг +
1791
+ * countdown (`expires_at` ИЛИ `duration_minutes` от первого открытия
1792
+ * пейвола, см. clientStorage `pw-offer-{id}-start`).
1793
+ *
1794
+ * Read-only — НЕ записывает start для `duration_minutes`-офферов. Запись
1795
+ * стартует только когда модалка реально открыта (renderer'ом). До этого
1796
+ * `getOfferForPrice` вернёт `null` для duration-only офферов, чтобы
1797
+ * страницы-хосты вне модалки (pricing, landing) не активировали countdown
1798
+ * раньше времени.
1799
+ *
1800
+ * Хост-странице нужен countdown, который тикает каждую секунду — для
1801
+ * этого использовать React-хук `usePaywallOffer(priceId)` из sdk-react
1802
+ * либо обёртку поверх `setInterval(1000)` + повторный вызов этого метода.
1803
+ */
1804
+ getOfferForPrice(priceId: string): ResolvedOffer | null;
1667
1805
  /** Снимок текущего «языка юзера» — proxy над `billing.getUserLanguage()`.
1668
1806
  * Используй, чтобы синхронизировать i18n host'а с тем, что фактически
1669
1807
  * показывает пейвол. См. подробности в `BillingClient.getUserLanguage`. */
@@ -1783,6 +1921,51 @@ export declare class QuotaExceededError extends PaywallError {
1783
1921
  });
1784
1922
  }
1785
1923
 
1924
+ /** Safe browser localStorage getter — returns null in SSR / private mode. */
1925
+ export declare function readBrowserOfferStart(offerId: string): string | null;
1926
+
1927
+ /**
1928
+ * Resolved view of a paywall offer — what host UI actually needs to render
1929
+ * a strike-through price + countdown without re-implementing the math.
1930
+ *
1931
+ * `remainingMs` ticks down with wall-clock time and reaches 0 on expiry.
1932
+ * `totalMs` stays constant — useful for progress bars / share-of-time UX.
1933
+ * `expiresAt` is the Date.now()-comparable epoch ms of expiry.
1934
+ *
1935
+ * For offers without an expiry mechanism (no `expires_at` and no
1936
+ * `duration_minutes`), `remainingMs`/`totalMs`/`expiresAt` are all `null`,
1937
+ * but the resolved view is still returned — discount badge / strike-through
1938
+ * still make sense for "perpetual sale" offers.
1939
+ */
1940
+ export declare interface ResolvedOffer {
1941
+ offer: PaywallOffer;
1942
+ discountPercent: number;
1943
+ remainingMs: number | null;
1944
+ totalMs: number | null;
1945
+ expiresAt: number | null;
1946
+ }
1947
+
1948
+ /** Compute the resolved view of an offer. Pure, no side-effects. */
1949
+ export declare function resolveOffer(offer: PaywallOffer, opts?: ResolveOfferOptions): ResolvedOffer | null;
1950
+
1951
+ export declare interface ResolveOfferOptions {
1952
+ /** Current epoch ms. Inject for deterministic tests; default `Date.now()`. */
1953
+ now?: number;
1954
+ /**
1955
+ * Synchronous reader for the `duration_minutes` start-timestamp ISO string.
1956
+ * Host passes a closure over its sync storage (browser → `localStorage`,
1957
+ * memory → in-process map). Return `null` if no start has been recorded.
1958
+ *
1959
+ * Intentionally synchronous, because consumers call this from UI render —
1960
+ * an async StorageAdapter would force every price card to suspend.
1961
+ *
1962
+ * If omitted, `duration_minutes`-only offers return `expiresAt = null`,
1963
+ * which makes the resolved view treat them as "not yet started" (the
1964
+ * renderer is responsible for writing the start on first paywall view).
1965
+ */
1966
+ readStart?: (offerId: string) => string | null;
1967
+ }
1968
+
1786
1969
  export declare const SDK_VERSION = "3.0.0-alpha.0";
1787
1970
 
1788
1971
  export declare type SignUpResult = {
package/dist/index.js CHANGED
@@ -1,19 +1,23 @@
1
- import { ApiClient as t, ApiGatewayClient as a, AuthClient as i, BillingClient as l, EventTracker as o, PaywallError as n, QuotaExceededError as E, SDK_VERSION as s, STORAGE_KEYS as S, createStorage as c, ensureVisitorId as d, generateVisitorId as g } from "./core.js";
2
- import { P as y, b as A } from "./chunks/PaywallUI-BhQpWSWN.js";
1
+ import { A as r, a as s, b as t, B as i, E as o, P as l, Q as f, S as n, c as S, d as E, e as d, f as c, h as p, o as A, r as g, i as y } from "./chunks/index-CLB1AgLg.js";
2
+ import { P as b, b as w } from "./chunks/PaywallUI-CDlJL5Em.js";
3
3
  export {
4
- t as ApiClient,
5
- a as ApiGatewayClient,
6
- i as AuthClient,
7
- l as BillingClient,
4
+ r as ApiClient,
5
+ s as ApiGatewayClient,
6
+ t as AuthClient,
7
+ i as BillingClient,
8
8
  o as EventTracker,
9
- n as PaywallError,
10
- y as PaywallUI,
11
- E as QuotaExceededError,
12
- s as SDK_VERSION,
9
+ l as PaywallError,
10
+ b as PaywallUI,
11
+ f as QuotaExceededError,
12
+ n as SDK_VERSION,
13
13
  S as STORAGE_KEYS,
14
- A as blockRegistry,
15
- c as createStorage,
14
+ w as blockRegistry,
15
+ E as createStorage,
16
16
  d as ensureVisitorId,
17
- g as generateVisitorId
17
+ c as findApplicableOffer,
18
+ p as generateVisitorId,
19
+ A as offerStartStorageKey,
20
+ g as readBrowserOfferStart,
21
+ y as resolveOffer
18
22
  };
19
23
  //# sourceMappingURL=index.js.map
package/dist/ui.cjs CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./chunks/PaywallUI-C7Wp2TiR.js");exports.PaywallUI=e.PaywallUI;exports.blockRegistry=e.blockRegistry;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./chunks/PaywallUI-CCwYgagL.js");exports.PaywallUI=e.PaywallUI;exports.blockRegistry=e.blockRegistry;
2
2
  //# sourceMappingURL=ui.cjs.map
package/dist/ui.d.ts CHANGED
@@ -623,6 +623,13 @@ declare class BillingClient {
623
623
  }): Promise<PaywallPrice[]>;
624
624
  /** Sync-снимок цен из последнего bootstrap'а. null = ещё не загружали. */
625
625
  getCachedPrices(): PaywallPrice[] | null;
626
+ /** Sync-снимок офферов из последнего bootstrap'а. null = bootstrap ещё не
627
+ * загружали, пустой массив = бэк отдал пейвол без офферов. Бэк уже
628
+ * применил серверный таргетинг (target_countries / target_emails /
629
+ * targeting_mode из offer_settings) — наружу выезжает только то, что
630
+ * применимо к текущему юзеру. Клиентская сторона остаётся ответственной
631
+ * за price_id-matching и countdown (см. core/offer.ts → resolveOffer). */
632
+ getCachedOffers(): PaywallOffer[] | null;
626
633
  /**
627
634
  * Снимок того, какой язык SDK сейчас считает «языком юзера». Полезно для
628
635
  * синхронизации i18n хоста с тем, что фактически показывает пейвол — чтобы
@@ -749,6 +756,17 @@ declare class BillingClient {
749
756
  errorUrl?: string;
750
757
  shopUrl?: string;
751
758
  trialDays?: number;
759
+ /** Активный offer для этой цены — резолвится host'ом через
760
+ * `paywall.getOfferForPrice(priceId)?.offer.id` или
761
+ * `findApplicableOffer(client.getCachedOffers(), priceId)?.id`. Без
762
+ * явной передачи бэк сделает auto-resolve по email — но только для
763
+ * end_date-офферов. duration_minutes-офферы тикают в clientStorage и
764
+ * сервер их не видит: для них offerId ОБЯЗАН прийти от клиента, иначе
765
+ * скидка не применится на чекауте, хотя UI её показывал.
766
+ *
767
+ * Передавать offer-id всегда безопасно — бэк сам проверит applicable
768
+ * ли offer к этому юзеру (страна/email/режим) и игнорирует если нет. */
769
+ offerId?: string;
752
770
  /**
753
771
  * Stage 1 защиты от дубликатов покупок. Идемпотентный ключ запроса
754
772
  * (UUID). Повторный вызов с тем же ключом вернёт тот же checkout-URL
@@ -775,7 +793,9 @@ declare class BillingClient {
775
793
  * инвойсы). Опен-флоу управляется host'ом:
776
794
  *
777
795
  * ```ts
778
- * const { url } = await billing.getCustomerPortalUrl();
796
+ * const { url } = await billing.getCustomerPortalUrl({
797
+ * returnUrl: 'https://your-app.com/account'
798
+ * });
779
799
  * window.open(url, '_blank');
780
800
  * ```
781
801
  *
@@ -787,6 +807,14 @@ declare class BillingClient {
787
807
  */
788
808
  getCustomerPortalUrl(opts?: {
789
809
  signal?: AbortSignal;
810
+ /** URL для return-button у провайдера (Stripe «Return to ...», Paddle
811
+ * и Chargebee redirect_url'ы). Передавай туда страницу-аккаунт твоего
812
+ * app'а — `https://your-app.com/account`. Без явного returnUrl бэк
813
+ * применяет fallback в порядке: `paywall_settings.shop_url` →
814
+ * custom_domain пейвола → NEXT_PUBLIC_ONLINE_ORIGIN (последнее — это
815
+ * страница в самом online-сервисе, годится только для legacy
816
+ * v2-iframe-флоу). */
817
+ returnUrl?: string;
790
818
  }): Promise<{
791
819
  url: string;
792
820
  }>;
@@ -1274,8 +1302,10 @@ declare interface PaywallOffer {
1274
1302
  expires_at: string | null;
1275
1303
  /** Относительный таймер: сколько минут offer живёт **от первого просмотра
1276
1304
  * пейвола** данным юзером. Старт хранится в clientStorage под ключом
1277
- * `pw-offer-{id}-start`, по истечении auto-cleanup. Используется, когда
1278
- * бэк хочет показывать "remaining time" без жёсткой server-time, а считать
1305
+ * `pw-offer-{id}-start` и остаётся там **после** expiry — это
1306
+ * forever-marker, чтобы юзер не мог «фармить» offer (re-open пейвола
1307
+ * не запускает countdown заново). Используется, когда бэк хочет
1308
+ * показывать "remaining time" без жёсткой server-time, а считать
1279
1309
  * относительно сессии юзера. expires_at имеет приоритет если задано. */
1280
1310
  duration_minutes?: number | null;
1281
1311
  price_id: string | null;
@@ -1391,12 +1421,25 @@ declare type PaywallStateListener = (state: PaywallStateSnapshot) => void;
1391
1421
  * один call onState; повторы (`useSyncExternalStore`-friendly).
1392
1422
  */
1393
1423
  export declare interface PaywallStateSnapshot {
1394
- /** Модалка отрендерена и видна. False — closed (или ещё не открывалась). */
1424
+ /** Модалка отрендерена и видна. False — closed (или ещё не открывалась).
1425
+ * Может быть false при `processing=true` — direct-checkout (paywall.checkout)
1426
+ * делает bootstrap + createCheckout headless до того, как решит надо ли
1427
+ * монтировать модалку. */
1395
1428
  open: boolean;
1396
1429
  /** Что показывается в модалке. null когда `open=false`. */
1397
1430
  view: 'loading' | 'error' | 'layout' | 'auth' | 'support' | 'awaiting_payment' | 'popup_blocked' | 'purchased' | null;
1398
1431
  /** Заполнено только когда `view === 'error'`. */
1399
1432
  error: PaywallError | null;
1433
+ /** SDK делает фоновую работу для `paywall.checkout(priceId)` — bootstrap,
1434
+ * visibility/trial-gates, createCheckout — до того, как реально нужна
1435
+ * UI-модалка. Хост на этой фазе может задизейблить свою кнопку и
1436
+ * показать спиннер прямо на ней, чтобы у юзера не было ощущения «клик
1437
+ * ничего не сделал». Сбрасывается в false сразу после mountAndShow
1438
+ * (модалка взяла на себя UI), либо после headless reject (already-paid,
1439
+ * ошибка createCheckout без модалки). Для `paywall.open()`-flow всегда
1440
+ * false: там модалка появляется мгновенно со своим LoadingView и
1441
+ * отдельный «processing» не нужен. */
1442
+ processing: boolean;
1400
1443
  }
1401
1444
 
1402
1445
  export declare class PaywallUI {
@@ -1416,6 +1459,12 @@ export declare class PaywallUI {
1416
1459
  private watcher;
1417
1460
  private tracker;
1418
1461
  private purchased;
1462
+ /** View, с которым последний раз монтировалась модалка. Гейтит аналитику
1463
+ * `paywall_opened`/`paywall_viewed` на реальный пейвол (`'layout'`): открытие
1464
+ * support / standalone-auth / awaiting_payment эмитит публичный `'open'` и
1465
+ * `'ready'`, но это не «пейвол открыт/просмотрен» — иначе саппорт-клик шлёт
1466
+ * ложный `paywall_opened`. */
1467
+ private lastMountedView;
1419
1468
  /** Lazy-инстанс TrialStore. Резолвится при первом open(), когда уже знаем
1420
1469
  * `bootstrap.settings.trial`. null — триал отключён в конфиге пейвола. */
1421
1470
  private trialStore;
@@ -1537,6 +1586,65 @@ export declare class PaywallUI {
1537
1586
  * Без managed-auth — no-op.
1538
1587
  */
1539
1588
  openSignup(opts?: OpenOptions): void;
1589
+ /**
1590
+ * Direct-checkout: создать checkout-URL по конкретной цене и сразу открыть
1591
+ * платёжного провайдера, минуя layout с тарифами. Полезно когда
1592
+ * host-приложение рендерит pricing-карточки/таблицу собственным UI и
1593
+ * хочет, чтобы клик по «Buy / Get this plan» вёл прямо в Stripe/Paddle.
1594
+ *
1595
+ * **Late-mount UX.** В отличие от `open()`, модалка не появляется во время
1596
+ * фоновой работы (bootstrap + visibility/trial gates + createCheckout).
1597
+ * Хост на этой фазе показывает busy-state прямо на своей кнопке (через
1598
+ * `state.processing === true` из `paywall.getState()` — или автоматически
1599
+ * через `<PaywallButton priceId>` в sdk-react). Модалка монтируется
1600
+ * ТОЛЬКО когда реально нужна UI:
1601
+ * - `checkout_mode='preauth'` + managed-auth + не залогинен → auth-gate
1602
+ * (форма signin'а); после успеха auto-resume в createCheckout.
1603
+ * - popup провайдера заблокирован браузером → popup_blocked view с
1604
+ * retry-кнопкой под fresh user gesture.
1605
+ * - popup открылся успешно → awaiting_payment view (индикатор «оплати
1606
+ * в новой вкладке» + I've paid).
1607
+ *
1608
+ * Что эмитится без модалки:
1609
+ * - `purchase_completed{restored:true, priceId}` когда юзер уже подписан
1610
+ * (cached user, fresh bootstrap, или 409 hasActivePurchase от бэка) —
1611
+ * headless reject;
1612
+ * - `error` когда createCheckout упал или identity.email отсутствует;
1613
+ * - `visibility_blocked` / `trial_blocked` — стандартные gate-эвенты.
1614
+ *
1615
+ * Что эмитится одновременно с модалкой:
1616
+ * - `checkout_started{priceId, url, acquiring}` ровно когда headless URL
1617
+ * получен, ДО mount'а awaiting_payment/popup_blocked.
1618
+ *
1619
+ * Offer (countdown-скидка) автоматически резолвится из cached offers'ов
1620
+ * через `getOfferForPrice(priceId)` и передаётся в createCheckout как
1621
+ * `offerId` — чтобы duration_minutes-офферы тоже применились на бэке
1622
+ * (для них нет server-side таймера, без явного offer-id скидка теряется).
1623
+ *
1624
+ * Требования:
1625
+ * - `identity.email` должен быть выставлен (через `opts.identity`, либо
1626
+ * managed-auth, либо ранний `setIdentity`/`paywall.open({identity})`).
1627
+ * Без email бэк `/start-checkout` 400'нёт; SDK эмитнет `error`.
1628
+ * - В `checkout_mode='preauth'` без managed-auth — backend требует
1629
+ * email-юзера; убедись что `identity.email` явно задан.
1630
+ *
1631
+ * Без модалки совсем (когда host рендерит свой awaiting-payment экран) —
1632
+ * используй `paywall.billing.createCheckout({priceId, offerId})` напрямую,
1633
+ * но тогда auth-gate / popup_blocked / awaiting_payment придётся рисовать
1634
+ * самостоятельно.
1635
+ */
1636
+ checkout(priceId: string, opts?: OpenOptions): void;
1637
+ /** Headless prep-work для `checkout(priceId, opts)`: bootstrap → gates →
1638
+ * preauth check → createCheckout → mount модалки с финальным view.
1639
+ * Вынесено отдельным методом ради чистого async/await flow вместо вложенных
1640
+ * then-chain'ов (5+ ветвей). Любая ошибка не пробрасывается наружу: эмитим
1641
+ * через `paywall.emit('error')` и выходим — host подписан на `error`-event. */
1642
+ private runDirectCheckout;
1643
+ /** Trial-check без mount'а (для late-mount direct-checkout). Возвращает
1644
+ * true если trial заблокировал — caller должен прекратить flow. На любой
1645
+ * storage-ошибке log+продолжаем (не блокируем продажу). */
1646
+ private checkTrialBeforeCheckout;
1647
+ private applyProcessing;
1540
1648
  /**
1541
1649
  * Headless anonymous signin без открытия модалки. Внутри:
1542
1650
  * idempotent (если уже анон — instant return) → resume через сохранённый
@@ -1603,6 +1711,26 @@ export declare class PaywallUI {
1603
1711
  }): Promise<PaywallPrice[]>;
1604
1712
  /** Sync-снимок цен. null — bootstrap ещё не загружали. */
1605
1713
  getCachedPrices(): PaywallPrice[] | null;
1714
+ /** Sync-снимок офферов. null = bootstrap не загружали, [] = пейвол без офферов.
1715
+ * Бэк уже применил серверный targeting (страны/email/режим) — наружу
1716
+ * выезжает только то, что применимо к текущему юзеру. */
1717
+ getCachedOffers(): PaywallOffer[] | null;
1718
+ /**
1719
+ * Резолвит активный offer для конкретной цены: price_id-таргетинг +
1720
+ * countdown (`expires_at` ИЛИ `duration_minutes` от первого открытия
1721
+ * пейвола, см. clientStorage `pw-offer-{id}-start`).
1722
+ *
1723
+ * Read-only — НЕ записывает start для `duration_minutes`-офферов. Запись
1724
+ * стартует только когда модалка реально открыта (renderer'ом). До этого
1725
+ * `getOfferForPrice` вернёт `null` для duration-only офферов, чтобы
1726
+ * страницы-хосты вне модалки (pricing, landing) не активировали countdown
1727
+ * раньше времени.
1728
+ *
1729
+ * Хост-странице нужен countdown, который тикает каждую секунду — для
1730
+ * этого использовать React-хук `usePaywallOffer(priceId)` из sdk-react
1731
+ * либо обёртку поверх `setInterval(1000)` + повторный вызов этого метода.
1732
+ */
1733
+ getOfferForPrice(priceId: string): ResolvedOffer | null;
1606
1734
  /** Снимок текущего «языка юзера» — proxy над `billing.getUserLanguage()`.
1607
1735
  * Используй, чтобы синхронизировать i18n host'а с тем, что фактически
1608
1736
  * показывает пейвол. См. подробности в `BillingClient.getUserLanguage`. */
@@ -1722,6 +1850,27 @@ declare class QuotaExceededError extends PaywallError {
1722
1850
  });
1723
1851
  }
1724
1852
 
1853
+ /**
1854
+ * Resolved view of a paywall offer — what host UI actually needs to render
1855
+ * a strike-through price + countdown without re-implementing the math.
1856
+ *
1857
+ * `remainingMs` ticks down with wall-clock time and reaches 0 on expiry.
1858
+ * `totalMs` stays constant — useful for progress bars / share-of-time UX.
1859
+ * `expiresAt` is the Date.now()-comparable epoch ms of expiry.
1860
+ *
1861
+ * For offers without an expiry mechanism (no `expires_at` and no
1862
+ * `duration_minutes`), `remainingMs`/`totalMs`/`expiresAt` are all `null`,
1863
+ * but the resolved view is still returned — discount badge / strike-through
1864
+ * still make sense for "perpetual sale" offers.
1865
+ */
1866
+ declare interface ResolvedOffer {
1867
+ offer: PaywallOffer;
1868
+ discountPercent: number;
1869
+ remainingMs: number | null;
1870
+ totalMs: number | null;
1871
+ expiresAt: number | null;
1872
+ }
1873
+
1725
1874
  declare type SignUpResult = {
1726
1875
  kind: 'signed_in';
1727
1876
  session: AuthSession;
package/dist/ui.js CHANGED
@@ -1,4 +1,4 @@
1
- import { P as o, b as r } from "./chunks/PaywallUI-BhQpWSWN.js";
1
+ import { P as o, b as r } from "./chunks/PaywallUI-CDlJL5Em.js";
2
2
  export {
3
3
  o as PaywallUI,
4
4
  r as blockRegistry
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@monetize.software/sdk",
3
- "version": "3.0.0-alpha.9",
3
+ "version": "3.0.0-beta.0",
4
4
  "description": "Monetize SDK — bundled billing client and paywall render engine for web and Chrome extensions",
5
5
  "type": "module",
6
6
  "sideEffects": false,