$paywalled_content = get_paywall_content();
if ( has_block( \Automattic\Jetpack\Extensions\Paywall\BLOCK_NAME ) ) {
if ( strpos( $the_content, \Automattic\Jetpack\Extensions\Paywall\BLOCK_HTML ) ) {
return strstr( $the_content, \Automattic\Jetpack\Extensions\Paywall\BLOCK_HTML, true ) . $paywalled_content;
// WordPress generates excerpts by either rendering or stripping blocks before invoking the `the_content` filter.
// In the context of generating an excerpt, the Paywall block specifically renders THE_EXCERPT_BLOCK.
if ( strpos( $the_content, \Automattic\Jetpack\Extensions\Paywall\THE_EXCERPT_BLOCK ) ) {
return strstr( $the_content, \Automattic\Jetpack\Extensions\Paywall\THE_EXCERPT_BLOCK, true );
return $paywalled_content;
* Gate access to comments. We want to close comments on private sites.
* @param bool $default_comments_open Default state of the comments_open filter.
* @param int $post_id Current post id.
function maybe_close_comments( $default_comments_open, $post_id ) {
if ( ! $default_comments_open || ! $post_id ) {
return $default_comments_open;
require_once JETPACK__PLUGIN_DIR . 'modules/memberships/class-jetpack-memberships.php';
return Jetpack_Memberships::user_can_view_post();
* Gate access to existing comments
* @param string $comment The comment.
function maybe_gate_existing_comments( $comment ) {
if ( empty( $comment ) ) {
require_once JETPACK__PLUGIN_DIR . 'modules/memberships/class-jetpack-memberships.php';
if ( Jetpack_Memberships::user_can_view_post() ) {
* Is the Jetpack_Token_Subscription_Service class loaded
function is_jetpack_token_subscription_service_loaded(): bool {
return class_exists( 'Automattic\Jetpack\Extensions\Premium_Content\Subscription_Service\Jetpack_Token_Subscription_Service' );
* Adds support for WP Super cache and Boost cache
function maybe_prevent_super_cache_caching() {
// Prevents cached page to be served if the Membership cookie is present
if ( is_jetpack_token_subscription_service_loaded() ) {
do_action( 'wpsc_add_cookie', Jetpack_Token_Subscription_Service::JWT_AUTH_TOKEN_COOKIE_NAME );
// Do not cache the page if user is auth with Membership token
if ( ! defined( 'DONOTCACHEPAGE' ) ) {
define( 'DONOTCACHEPAGE', true );
* Returns paywall content blocks
function get_paywall_content() {
if ( Jetpack_Memberships::user_is_pending_subscriber() ) {
return get_paywall_blocks_subscribe_pending();
if ( doing_filter( 'get_the_excerpt' ) ) {
return get_paywall_blocks();
* Returns the current URL.
* TODO: Copied from https://github.com/Automattic/jetpack/blob/bb885061dc3ee7a80a78a5f0116ab3fcebfddb09/projects/packages/boost-core/src/lib/class-url.php#L39
* TODO: Move to a shared package
function get_current_url() {
// Fallback to the site URL if we're unable to determine the URL from $_SERVER global.
$current_url = site_url();
if ( isset( $_SERVER ) && is_array( $_SERVER ) ) {
// phpcs:disable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Sanitization happens at the end
$scheme = isset( $_SERVER['HTTPS'] ) && 'on' === $_SERVER['HTTPS'] ? 'https' : 'http';
$host = ! empty( $_SERVER['HTTP_HOST'] ) ? wp_unslash( $_SERVER['HTTP_HOST'] ) : null;
$path = ! empty( $_SERVER['REQUEST_URI'] ) ? wp_unslash( $_SERVER['REQUEST_URI'] ) : '';
// Support for local plugin development and testing using ngrok.
if ( ! empty( $_SERVER['HTTP_X_ORIGINAL_HOST'] ) && str_contains( $_SERVER['HTTP_X_ORIGINAL_HOST'], 'ngrok.io' ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput -- This is validating.
$host = wp_unslash( $_SERVER['HTTP_X_ORIGINAL_HOST'] );
// phpcs:enable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
$current_url = esc_url_raw( sprintf( '%s://%s%s', $scheme, $host, $path ) );
* Get the submit button text based on the subscription status.
* @param array $data Array containing block view data.
function get_submit_button_text( $data ) {
if ( ! Jetpack_Memberships::is_current_user_subscribed() ) {
return $data['submit_button_text'];
if ( ! Jetpack_Memberships::user_can_view_post() ) {
return $data['submit_button_text_upgrade'];
return '✓ ' . $data['submit_button_text_subscribed'];
* Returns true if there are no more tiers to upgrade to.
function is_top_subscription() {
if ( ! Jetpack_Memberships::is_current_user_subscribed() ) {
if ( ! Jetpack_Memberships::user_can_view_post() ) {
* Sanitize the submit button text.
* @param string $text String containing the submit button text.
function sanitize_submit_text( $text ) {
html_entity_decode( $text, ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401 ),
Jetpack_Subscriptions_Widget::$allowed_html_tags_for_submit_button
* Returns paywall content blocks if user is not authenticated
function get_paywall_blocks() {
$custom_paywall = apply_filters( 'jetpack_custom_paywall_blocks', false );
if ( ! empty( $custom_paywall ) ) {
if ( ! Request::is_frontend() ) { // emails
return get_paywall_simple();
require_once JETPACK__PLUGIN_DIR . 'modules/memberships/class-jetpack-memberships.php';
$is_paid_post = is_paid_post();
$is_paid_subscriber = Jetpack_Memberships::user_is_paid_subscriber();
$access_heading = $is_paid_subscriber
? esc_html__( 'Upgrade to continue reading', 'jetpack' )
: esc_html__( 'Subscribe to continue reading', 'jetpack' );
$subscribe_text = $is_paid_post
// translators: %s is the name of the site.
? esc_html__( 'Upgrade to get access to the rest of this post and other exclusive content.', 'jetpack' )
: esc_html__( 'Become a paid subscriber to get access to the rest of this post and other exclusive content.', 'jetpack' )
// translators: %s is the name of the site.
: esc_html__( 'Subscribe to get access to the rest of this post and other subscriber-only content.', 'jetpack' );
if ( ( new Host() )->is_wpcom_simple() ) {
// We cannot use wpcom_logmein_redirect_url since it returns redirect URL when user is already logged in.
$login_link = add_query_arg(
'redirect_to' => rawurlencode( get_current_url() ),
'blog_id' => get_current_blog_id(),
'https://wordpress.com/log-in/link'
$switch_accounts_link = wp_logout_url( $login_link );
$login_block = '<!-- wp:paragraph {"align":"center","style":{"typography":{"fontSize":"14px"}}} -->
<p class="has-text-align-center" style="font-size:14px">
<a href="' . $switch_accounts_link . '">' . __( 'Switch accounts', 'jetpack' ) . '</a>
$access_question = $is_paid_post ? esc_html__( 'Already a paid subscriber?', 'jetpack' ) : esc_html__( 'Already a subscriber?', 'jetpack' );
$login_block = '<!-- wp:group {"style":{"typography":{"fontSize":"14px"}},"layout":{"type":"flex","justifyContent":"center"}} -->
<div class="wp-block-group" style="font-size:14px">
<!-- wp:jetpack/subscriber-login {"logInLabel":"' . $access_question . '"} /-->
$lock_svg = plugins_url( 'images/lock-paywall.svg', JETPACK__PLUGIN_FILE );
<!-- wp:group {"style":{"border":{"width":"1px","radius":"4px"},"spacing":{"padding":{"top":"32px","bottom":"32px","left":"32px","right":"32px"}}},"borderColor":"primary","className":"jetpack-subscribe-paywall","layout":{"type":"constrained","contentSize":"400px"}} -->
<div class="wp-block-group jetpack-subscribe-paywall has-border-color has-primary-border-color" style="border-width:1px;border-radius:4px;padding-top:32px;padding-right:32px;padding-bottom:32px;padding-left:32px">
<!-- wp:image {"align":"center","width":24,"height":24,"sizeSlug":"large","linkDestination":"none"} -->
<figure class="wp-block-image aligncenter size-large is-resized"><img src="' . $lock_svg . '" alt="" width="24" height="24"/></figure>
<!-- wp:heading {"textAlign":"center","style":{"typography":{"fontStyle":"normal","fontWeight":"600","fontSize":"24px"},"layout":{"selfStretch":"fit"}}} -->
<h2 class="wp-block-heading has-text-align-center" style="font-size:24px;font-style:normal;font-weight:600">' . $access_heading . '</h2>
<!-- wp:paragraph {"align":"center","style":{"typography":{"fontSize":"14px"},"spacing":{"margin":{"top":"10px","bottom":"10px"}}}} -->
<p class="has-text-align-center" style="margin-top:10px;margin-bottom:10px;font-size:14px">' . $subscribe_text . '</p>
<!-- wp:jetpack/subscriptions {"borderRadius":50,"borderColor":"primary","className":"is-style-compact","isPaidSubscriber":' . ( $is_paid_subscriber ? 'true' : 'false' ) . '} /-->
* Returns true if user is auth for subscriptions check, otherwise returns false.
function is_user_auth(): bool {
if ( ( new Host() )->is_wpcom_simple() && is_user_logged_in() ) {
if ( current_user_can( 'manage_options' ) ) {
if ( is_jetpack_token_subscription_service_loaded() ) {
if ( Jetpack_Token_Subscription_Service::has_token_from_cookie() ) {
* Returns `true` if the post is a paid post.
function is_paid_post(): bool {
require_once JETPACK__PLUGIN_DIR . 'modules/memberships/class-jetpack-memberships.php';
// Make sure Stripe is connected and the post is marked for paid subscribers.
if ( Jetpack_Memberships::has_connected_account() && is_jetpack_token_subscription_service_loaded() ) {
return Jetpack_Memberships::get_post_access_level() === Jetpack_Token_Subscription_Service::POST_ACCESS_LEVEL_PAID_SUBSCRIBERS;
* Returns true if the post is a subscribers post.
function is_subscribers_post(): bool {
require_once JETPACK__PLUGIN_DIR . 'modules/memberships/class-jetpack-memberships.php';
// Make sure Stripe is connected and the post is marked for paid subscribers.
if ( Jetpack_Memberships::has_connected_account() && is_jetpack_token_subscription_service_loaded() ) {
return Jetpack_Memberships::get_post_access_level() === Jetpack_Token_Subscription_Service::POST_ACCESS_LEVEL_SUBSCRIBERS;
* Returns paywall content blocks when email confirmation is pending
function get_paywall_blocks_subscribe_pending() {
$subscribe_email = Jetpack_Memberships::get_current_user_email();
/** This filter is documented in \Automattic\Jetpack\Forms\ContactForm\Contact_Form */
if ( is_wpcom() || false !== apply_filters( 'jetpack_auto_fill_logged_in_user', false ) ) {
$current_user = wp_get_current_user();
if ( ! empty( $current_user->user_email ) ) {
$subscribe_email = $current_user->user_email;
$access_heading = esc_html__( 'Confirm your subscription to continue reading', 'jetpack' );
/* translators: %s: email address */
$subscribe_text = sprintf( esc_html__( 'Head to your inbox and confirm your email address %s.', 'jetpack' ), $subscribe_email );
$lock_svg = plugins_url( 'images/lock-paywall.svg', JETPACK__PLUGIN_FILE );
<!-- wp:group {"style":{"border":{"width":"1px","radius":"4px"},"spacing":{"padding":{"top":"32px","bottom":"32px","left":"32px","right":"32px"}}},"borderColor":"primary","className":"jetpack-subscribe-paywall","layout":{"type":"constrained","contentSize":"400px"}} -->
<div class="wp-block-group jetpack-subscribe-paywall has-border-color has-primary-border-color" style="border-width:1px;border-radius:4px;padding-top:32px;padding-right:32px;padding-bottom:32px;padding-left:32px">
<!-- wp:image {"align":"center","width":24,"height":24,"sizeSlug":"large","linkDestination":"none"} -->
<figure class="wp-block-image aligncenter size-large is-resized"><img src="' . $lock_svg . '" alt="" width="24" height="24"/></figure>
<!-- wp:heading {"textAlign":"center","style":{"typography":{"fontStyle":"normal","fontWeight":"600","fontSize":"24px", "maxWidth":"initial"},"layout":{"selfStretch":"fit"}}} -->
<h2 class="wp-block-heading has-text-align-center" style="font-size:24px;font-style:normal;font-weight:600;max-width:initial">' . $access_heading . '</h2>
<!-- wp:paragraph {"align":"center","style":{"typography":{"fontSize":"14px"},"spacing":{"margin":{"top":"10px","bottom":"10px"}}}} -->
<p class="has-text-align-center" style="margin-top:10px;margin-bottom:10px;font-size:14px">' . $subscribe_text . '</p>
* Return content for non frontend views like Reader, emails.
function get_paywall_simple(): string {
$is_paid_post = is_paid_post();
$is_subscribers_post = is_subscribers_post();
$is_subscriber = is_jetpack_memberships_loaded() && Jetpack_Memberships::is_current_user_subscribed();
$paywall_heading = esc_html__( 'Subscribe to keep reading', 'jetpack' );
if ( $is_subscribers_post && ! $is_subscriber ) {
$paywall_description = esc_html__( "It's a subscribers only post. Subscribe to get access to the rest of this post and other subscriber-only content.", 'jetpack' );
$paywall_action_btn = esc_html__( 'Subscribe', 'jetpack' );
} elseif ( $is_paid_post && $is_subscriber ) {
$paywall_description = esc_html__( "You're currently a free subscriber. Upgrade your subscription to get access to the rest of this post and other paid-subscriber only content.", 'jetpack' );
$paywall_action_btn = esc_html__( 'Upgrade subscription', 'jetpack' );
// - For paid post when the user is not a subscriber.
// - Default for all other cases.
$paywall_description = esc_html__( 'Become a paid subscriber to get access to the rest of this post and other exclusive content.', 'jetpack' );
$paywall_action_btn = esc_html__( 'Subscribe', 'jetpack' );
<div class="wp-block-columns jetpack-paywall-simple" style="display: inline-block; width: 90%">
<div class="wp-block-column" style="background-color: #F6F7F7; padding: 32px; 24px;">
<h2 class="has-text-align-center" style="margin: 0 0 12px; font-weight: 600;">' . $paywall_heading . '</h2>
<p class="has-text-align-center"
style="text-align: center;
font-family: \'SF Pro Text\', sans-serif;
' . $paywall_description . '
<div class="wp-block-buttons" style="text-align: center;">
<div class="wp-block-button" style="display: inline-block; margin: 10px 0; border-style: none; padding: 0;">
<a href="' . esc_url( get_post_permalink() ) . '" class="wp-block-button__link wp-element-button"
data-wpcom-track data-tracks-link-desc="paywall-email-click"
style="display: inline-block;
background-color: #3858e9;
font-family: \'SF Pro Display\', sans-serif;
text-align: center;">' . $paywall_action_btn . '</a>