user_for( $subscription ); if ( ! $user ) { return; } $status = Subscription_Status::tryFrom( (string) $to ); if ( ! $status ) { return; } if ( $status->is_billable() ) { $this->apply( $user, (string) get_option( self::OPTION_ACTIVE, '' ) ); return; } if ( ! $subscription->in_grace() ) { $this->demote( $subscription ); } } public function demote( Subscription $subscription ): void { $user = $this->user_for( $subscription ); // Only demote once nothing else is keeping them in: a customer with two // subscriptions must not lose access because one of them lapsed. if ( $user && ! $this->has_another_live_subscription( $user->ID, $subscription->get_id() ) ) { $this->apply( $user, (string) get_option( self::OPTION_INACTIVE, '' ) ); } } public static function is_grantable( string $role ): bool { $object = '' === $role ? null : get_role( $role ); if ( ! $object ) { return false; } foreach ( self::ADMINISTRATIVE as $capability ) { if ( $object->has_cap( $capability ) ) { return false; } } return true; } /** @return array Role slug => translated name. */ public static function grantable_roles(): array { $roles = array(); foreach ( wp_roles()->get_names() as $slug => $label ) { if ( self::is_grantable( $slug ) ) { $roles[ $slug ] = translate_user_role( $label ); } } return $roles; } private function apply( \WP_User $user, string $role ): void { // A role saved by an older version never went through the dropdown. if ( ! self::is_grantable( $role ) ) { return; } // set_role() replaces every role, so staff would lose theirs by subscribing. foreach ( self::ADMINISTRATIVE as $capability ) { if ( user_can( $user, $capability ) ) { return; } } if ( in_array( $role, (array) $user->roles, true ) ) { return; } $user->set_role( $role ); } private function has_another_live_subscription( int $user_id, int $exclude_id ): bool { $ids = Subscription_Query::ids( array( 'customer_id' => $user_id, 'status' => array( Subscription_Status::Active->value, Subscription_Status::Trialling->value, Subscription_Status::PendingCancel->value ), 'limit' => 5, ) ); if ( array_diff( $ids, array( $exclude_id ) ) ) { return true; } foreach ( Subscription_Query::get( array( 'customer_id' => $user_id, 'status' => array( Subscription_Status::OnHold->value ), 'limit' => -1, ) ) as $held ) { if ( $held->get_id() !== $exclude_id && $held->in_grace() ) { return true; } } return false; } private function user_for( Subscription $subscription ): ?\WP_User { $user = $subscription->get_customer_id() ? get_userdata( $subscription->get_customer_id() ) : false; return $user instanceof \WP_User ? $user : null; } }