{"result":{"depiction_url":null,"edges":{"observes":{"objects":[{"created":"2026-10-22T10:28:13Z","object_id":{"id":1273,"is_a":["text","documentation","reference","notification"],"name":"doc_notification_rsc_pivot_done","title":"observe_rsc_pivot_done\/2","uri":"https:\/\/zotonic.com\/id\/1273"},"seq":1},{"created":"2026-10-22T10:28:13Z","object_id":{"id":1466,"is_a":["text","documentation","reference","notification"],"name":"doc_notification_admin_menu","title":"observe_admin_menu\/3","uri":"https:\/\/zotonic.com\/id\/1466"},"seq":2},{"created":"2026-10-22T10:28:13Z","object_id":{"id":1782,"is_a":["text","documentation","reference","notification"],"name":"doc_notification_export_resource_data","title":"observe_export_resource_data\/2","uri":"https:\/\/zotonic.com\/id\/1782"},"seq":3},{"created":"2026-10-22T10:28:13Z","object_id":{"id":1783,"is_a":["text","documentation","reference","notification"],"name":"doc_notification_export_resource_encode","title":"observe_export_resource_encode\/2","uri":"https:\/\/zotonic.com\/id\/1783"},"seq":4},{"created":"2026-10-22T10:28:13Z","object_id":{"id":1784,"is_a":["text","documentation","reference","notification"],"name":"doc_notification_export_resource_filename","title":"observe_export_resource_filename\/2","uri":"https:\/\/zotonic.com\/id\/1784"},"seq":5},{"created":"2026-10-22T10:28:13Z","object_id":{"id":1786,"is_a":["text","documentation","reference","notification"],"name":"doc_notification_export_resource_header","title":"observe_export_resource_header\/2","uri":"https:\/\/zotonic.com\/id\/1786"},"seq":6},{"created":"2026-10-22T10:28:13Z","object_id":{"id":1982,"is_a":["text","documentation","reference","notification"],"name":"doc_notification_search_query","title":"observe_search_query\/2","uri":"https:\/\/zotonic.com\/id\/1982"},"seq":7},{"created":"2026-10-22T10:28:13Z","object_id":{"id":1999,"is_a":["text","documentation","reference","notification"],"name":"doc_notification_rsc_merge","title":"observe_rsc_merge\/2","uri":"https:\/\/zotonic.com\/id\/1999"},"seq":8},{"created":"2026-10-22T10:28:13Z","object_id":{"id":2341,"is_a":["text","documentation","reference","notification"],"name":"doc_notification_search_query_term","title":"observe_search_query_term\/2","uri":"https:\/\/zotonic.com\/id\/2341"},"seq":9},{"created":"2026-10-22T10:28:13Z","object_id":{"id":2343,"is_a":["text","documentation","reference","notification"],"name":"doc_notification_acl_user_groups_modify","title":"observe_acl_user_groups_modify\/3","uri":"https:\/\/zotonic.com\/id\/2343"},"seq":10},{"created":"2026-10-22T10:28:13Z","object_id":{"id":2369,"is_a":["text","documentation","reference","notification"],"name":"doc_notification_survey_result_column_values","title":"observe_survey_result_column_values\/3","uri":"https:\/\/zotonic.com\/id\/2369"},"seq":11},{"created":"2026-10-22T10:28:13Z","object_id":{"id":2372,"is_a":["text","documentation","reference","notification"],"name":"doc_notification_survey_result_columns","title":"observe_survey_result_columns\/3","uri":"https:\/\/zotonic.com\/id\/2372"},"seq":12}],"predicate":{"id":2550,"is_a":["meta","predicate"],"name":"observes","title":{"_type":"trans","tr":{"en":"Observes"}},"uri":"https:\/\/zotonic.com\/id\/observes"}},"subject":{"objects":[{"created":"2026-10-22T10:28:13Z","object_id":{"id":2555,"is_a":["categorization","keyword","keyword_information_type"],"name":"zotonic_topic_reference","title":"Reference","uri":"https:\/\/zotonic.com\/id\/2555"},"seq":1},{"created":"2026-10-22T10:28:13Z","object_id":{"id":2562,"is_a":["categorization","keyword","keyword_audience"],"name":"zotonic_topic_site_administrator","title":"Site administrator","uri":"https:\/\/zotonic.com\/id\/2562"},"seq":2},{"created":"2026-10-22T10:28:13Z","object_id":{"id":2564,"is_a":["categorization","keyword","keyword_audience"],"name":"zotonic_topic_backend_developer","title":"Backend developer","uri":"https:\/\/zotonic.com\/id\/2564"},"seq":3},{"created":"2026-10-22T10:28:13Z","object_id":{"id":2589,"is_a":["categorization","keyword","keyword_domain"],"name":"zotonic_topic_identity_and_accounts","title":"Identity and accounts","uri":"https:\/\/zotonic.com\/id\/2589"},"seq":4},{"created":"2026-10-22T10:28:13Z","object_id":{"id":2592,"is_a":["categorization","keyword","keyword_domain"],"name":"zotonic_topic_authorization_and_access_control","title":"Authorization and access control","uri":"https:\/\/zotonic.com\/id\/2592"},"seq":5},{"created":"2026-10-22T10:28:13Z","object_id":{"id":2599,"is_a":["categorization","keyword","keyword_domain"],"name":"zotonic_topic_api_and_integration","title":"API and integration","uri":"https:\/\/zotonic.com\/id\/2599"},"seq":6},{"created":"2026-10-22T10:28:13Z","object_id":{"id":2603,"is_a":["categorization","keyword","keyword_domain"],"name":"zotonic_topic_configuration","title":"Configuration","uri":"https:\/\/zotonic.com\/id\/2603"},"seq":7},{"created":"2026-10-22T10:28:13Z","object_id":{"id":2635,"is_a":["categorization","keyword","keyword_architecture"],"name":"zotonic_topic_module","title":"Module","uri":"https:\/\/zotonic.com\/id\/2635"},"seq":8}],"predicate":{"id":308,"is_a":["meta","predicate"],"name":"subject","title":{"_type":"trans","tr":{"en":"Keyword"}},"uri":"http:\/\/purl.org\/dc\/elements\/1.1\/subject"}}},"id":3105,"is_a":["text","documentation","reference","module"],"links":[{"rel":"self","target":"https:\/\/zotonic.com\/.zotonic\/websub\/topic\/3105"},{"rel":"hub","target":"https:\/\/zotonic.com\/.zotonic\/websub"}],"medium":null,"medium_url":null,"name":"doc_external_9_mod_paysub","page_url":{"en":"https:\/\/zotonic.com\/docs\/3105\/mod_paysub","x-default":"https:\/\/zotonic.com\/docs\/3105\/mod_paysub"},"preview_url":null,"resource":{"version":41,"pivot_location_lat":null,"title":"mod_paysub","is_authoritative":true,"body":"<p>Manage paid memberships, customers, products, prices, subscriptions, invoices\nand payments. The bundled payment-service integration uses Stripe. Local\nrecords support membership checks and administration without a provider\nrequest on every page view.<\/p>\n<h2>Setup<\/h2>\n<p>Enable <code>mod_paysub<\/code>; its declared dependencies are <code>mod_authentication<\/code> and\n<code>mod_l10n<\/code>. Schema installation creates the payment tables and, when the\nACL user-group category exists, the <code>ug_inactive_member<\/code> group.\nConfigure these per-site <code>mod_paysub<\/code> settings:<\/p>\n<table class=\"table\"><thead><tr><th>Key<\/th><th>Default<\/th><th>Purpose<\/th><\/tr><\/thead><tbody><tr><td><code>stripe_api_key<\/code><\/td><td>Empty<\/td><td>API key for Stripe requests.<\/td><\/tr><tr><td><code>stripe_webhook_secret<\/code><\/td><td>Empty<\/td><td>Key for authenticating Stripe callbacks.<\/td><\/tr><tr><td><code>is_unpaid_access<\/code><\/td><td><code>false<\/code><\/td><td>Also grant subscription access for <code>incomplete_expired<\/code> and <code>unpaid<\/code> states.<\/td><\/tr><tr><td><code>is_no_customer_sync<\/code><\/td><td><code>false<\/code><\/td><td>Suppress copying provider customer billing changes back to resource fields.<\/td><\/tr><\/tbody><\/table>\n<p>Configure the provider webhook at the site&#39;s <code>\/paysub\/stripe\/webhook<\/code> URL\n(dispatch <code>paysub_stripe_webhook<\/code>). See <code>controller_paysub_stripe_webhook<\/code>\nfor supported events. A nonempty API key marks the Stripe integration as\nconfigured; this does not test connectivity or credentials.<\/p>\n<p>The Payments entries under the admin Modules menu provide a dashboard and\ninvoice, subscription, product and payment lists. Management uses administrator\nstatus or <code>use<\/code> permission for <code>mod_paysub<\/code>; individual operations can also\nrequire resource edit permission. Fetch products and prices through the admin\nor <code>mod_paysub:sync_products(Context)<\/code>, then associate products with user groups.\nThe direct sync function returns <code>ok<\/code> when Stripe is unconfigured, or the\nsynchronization result when configured.<\/p>\n<h2>Membership and synchronization<\/h2>\n<p>When a user context is initialized, subscription products add their associated\nuser groups. Subscriptions of organizations for which the user is a\n<code>hasmaincontact<\/code> contact also contribute groups, as do those organizations&#39;\n<code>hasusergroup<\/code> edges. If none are found, <code>ug_inactive_member<\/code> is added.\nAccess states are <code>incomplete<\/code>, <code>trialing<\/code>, <code>active<\/code> and <code>past_due<\/code>, plus\n<code>incomplete_expired<\/code> and <code>unpaid<\/code> when <code>is_unpaid_access<\/code> is enabled.<\/p>\n<p>Provider callbacks synchronize local payment records. Resource pivot updates\nschedule billing changes toward the provider; customer notifications copy\nchanged billing fields back unless <code>is_no_customer_sync<\/code> is enabled.\nResource merging and subscription moves update linked customer records.<\/p>\n<p>Use <code>m_paysub<\/code> for template data, checkout status and Erlang integration.\n<code>m_paysub_search<\/code> supplies administrative searches and resource query terms.\nThe module also supplies checkout completion\/cancellation pages, survey\npayment-status columns and a subscription spreadsheet export.<\/p>","slug":"mod_paysub","doc_module_config":[{"default":"&lt;&lt;&gt;&gt;","description":"The API key for Stripe. This is required to use Stripe as a payment service provider.","has_default":true,"key":"stripe_api_key","module":"mod_paysub","type":"string"},{"default":"&lt;&lt;&gt;&gt;","description":"The webhook secret for Stripe. This is required to verify the authenticity of webhook events from Stripe.","has_default":true,"key":"stripe_webhook_secret","module":"mod_paysub","type":"string"},{"default":"false","description":"If set to true, users with incomplete_expired and unpaid subscriptions will have access to the site. If set to false, users with unpaid subscriptions will not have access to the site. This can be useful if you want to allow users to access the site while they are in the process of renewing their subscription or if you want to give users a grace period after their subscription has expired.","has_default":true,"key":"is_unpaid_access","module":"mod_paysub","type":"boolean"},{"default":"false","description":"If set to true, the customer data will not be synced from the PSP to the resource. This means that changes to the customer data at the PSP will not be reflected in the resource. This can be useful if you want to manage the customer data manually or if you have a large number of customers and want to avoid syncing all data.","has_default":true,"key":"is_no_customer_sync","module":"mod_paysub","type":"boolean"}],"is_protected":false,"visible_for":0,"tz":"UTC","language":["en"],"doc_source_hash":"ef005afa6a52b990d1f052bb4dbcda6020c7a575e20db4974bca90ea72d12181","doc_module_observers":[{"name":"acl_user_groups_modify","page_name":"doc_notification_acl_user_groups_modify"},{"name":"admin_menu","page_name":"doc_notification_admin_menu"},{"name":"export_resource_data","page_name":"doc_notification_export_resource_data"},{"name":"export_resource_encode","page_name":"doc_notification_export_resource_encode"},{"name":"export_resource_filename","page_name":"doc_notification_export_resource_filename"},{"name":"export_resource_header","page_name":"doc_notification_export_resource_header"},{"name":"paysub_customer","page_name":"doc_notification_paysub_customer"},{"name":"rsc_merge","page_name":"doc_notification_rsc_merge"},{"name":"rsc_pivot_done","page_name":"doc_notification_rsc_pivot_done"},{"name":"search_query","page_name":"doc_notification_search_query"},{"name":"search_query_term","page_name":"doc_notification_search_query_term"},{"name":"survey_result_column_values","page_name":"doc_notification_survey_result_column_values"},{"name":"survey_result_columns","page_name":"doc_notification_survey_result_columns"}],"is_featured":false,"content_group_id":{"id":2551,"is_a":["meta","content_group"],"name":"content_group_imported_docs","title":"Imported documentation","uri":"https:\/\/zotonic.com\/id\/content_group_imported_docs"},"external_module_id":{"id":9,"is_a":[],"name":null,"title":null,"uri":"https:\/\/zotonic.com\/id\/9"},"git_url":"https:\/\/github.com\/zotonic\/zotonic_mod_paysub.git","category_id":{"id":320,"is_a":["meta","category"],"name":"module","title":"Modules","uri":"https:\/\/test.zotonic.com\/id\/320"},"doc_source_path":".\/src\/mod_paysub.erl","publication_start":"2026-10-22T10:28:13Z","github_url":"https:\/\/github.com\/zotonic\/zotonic_mod_paysub.git","pivot_location_lng":null,"doc_source_kind":"external","name":"doc_external_9_mod_paysub","is_unfindable":false,"is_published":true,"pivot_geocode":null,"external_module_title":"Paid Subscriptions","created":"2026-10-22T10:28:13Z","uri":null,"doc_status":"current","is_dependent":false,"is_external_module":true,"publication_end":"9999-06-01T00:00:00Z","modifier_id":{"id":1,"is_a":["person"],"name":"administrator","title":"Site Administrator","uri":"https:\/\/zotonic.com\/id\/1"},"privacy":0,"doc_source_commit":"3a4ee4d7434205778e4318c0c2e1576884e5678b","creator_id":{"id":1,"is_a":["person"],"name":"administrator","title":"Site Administrator","uri":"https:\/\/zotonic.com\/id\/1"},"modified":"2026-10-22T10:28:14Z","title_slug":"mod_paysub"},"uri":"https:\/\/zotonic.com\/id\/3105","uri_template":"https:\/\/zotonic.com\/id\/:id","websub":{"hub":"https:\/\/zotonic.com\/.zotonic\/websub","topic":"https:\/\/zotonic.com\/.zotonic\/websub\/topic\/3105"}},"status":"ok"}