$notice ) ) ); } /** * A post the current user may pay for: their own, and not yet live. */ public static function payable_post( $post_id ) { $post = $post_id ? get_post( $post_id ) : null; if ( ! $post || (int) $post->post_author !== get_current_user_id() || ! in_array( $post->post_type, postfee_post_types(), true ) ) { return null; } return in_array( $post->post_status, array( Posts::UNPAID, Posts::EXPIRED ), true ) ? $post : null; } /** * A post the current user may buy upgrades for. */ public static function upgradable_post( $post_id ) { $post = $post_id ? get_post( $post_id ) : null; if ( ! $post || (int) $post->post_author !== get_current_user_id() || ! in_array( $post->post_type, postfee_post_types(), true ) ) { return null; } return in_array( $post->post_status, array( 'publish', 'pending', Posts::EXPIRED ), true ) ? $post : null; } public static function editable_post( $post_id ) { $post = $post_id ? get_post( $post_id ) : null; if ( ! $post || (int) $post->post_author !== get_current_user_id() || ! in_array( $post->post_type, postfee_post_types(), true ) ) { return null; } return in_array( $post->post_status, array( 'publish', 'pending', Posts::UNPAID, Posts::EXPIRED ), true ) ? $post : null; } // phpcs:disable WordPress.Security.NonceVerification.Missing -- handle() checks the nonce before calling any of these. private static function submit_post() { $edit_id = isset( $_POST['postfee_post_id'] ) ? absint( $_POST['postfee_post_id'] ) : 0; $existing = $edit_id ? self::editable_post( $edit_id ) : null; $post_type = $existing ? $existing->post_type : ( isset( $_POST['postfee_post_type'] ) ? sanitize_key( $_POST['postfee_post_type'] ) : 'post' ); $title = isset( $_POST['postfee_title'] ) ? sanitize_text_field( wp_unslash( $_POST['postfee_title'] ) ) : ''; // The form is made of blocks, so the content field may have been left out. $has_content = isset( $_POST['postfee_content'] ); $content = $has_content ? wp_kses_post( wp_unslash( $_POST['postfee_content'] ) ) : ''; if ( $edit_id && ! $existing ) { self::$errors[] = __( 'You cannot edit this post.', 'postfee' ); } if ( ! in_array( $post_type, postfee_post_types(), true ) ) { self::$errors[] = __( 'This type of content cannot be submitted.', 'postfee' ); } if ( '' === $title ) { self::$errors[] = __( 'Please enter a title.', 'postfee' ); } if ( $has_content && '' === trim( wp_strip_all_tags( $content ) ) ) { self::$errors[] = __( 'Please enter some content.', 'postfee' ); } if ( self::$errors ) { return; } $data = array( 'post_title' => $title ); if ( $has_content ) { $data['post_content'] = $content; } if ( $existing ) { $data['ID'] = $existing->ID; // Edits to a live post go back through review when review is on. if ( 'publish' === $existing->post_status && 'pending' === postfee_setting( 'paid_status' ) ) { $data['post_status'] = 'pending'; } $post_id = wp_update_post( wp_slash( $data ), true ); } else { $data['post_type'] = $post_type; $data['post_status'] = Posts::UNPAID; $data['post_author'] = get_current_user_id(); $post_id = wp_insert_post( wp_slash( $data ), true ); } if ( is_wp_error( $post_id ) ) { self::$errors[] = $post_id->get_error_message(); return; } self::save_terms( $post_id, $post_type ); self::save_image( $post_id ); do_action( 'postfee_post_submitted', $post_id, (bool) $existing ); if ( $existing ) { self::to_dashboard( 'saved' ); } if ( Fulfillment::try_credit( $post_id ) ) { self::to_dashboard( 'credit_used' ); } self::redirect( postfee_page_url( 'submit', array( 'postfee_pay' => $post_id ) ) ); } private static function save_terms( $post_id, $post_type ) { $taxonomies = get_object_taxonomies( $post_type, 'objects' ); if ( isset( $_POST['postfee_terms'] ) && is_array( $_POST['postfee_terms'] ) ) { foreach ( array_map( 'absint', wp_unslash( $_POST['postfee_terms'] ) ) as $taxonomy => $term_id ) { $taxonomy = sanitize_key( $taxonomy ); if ( ! isset( $taxonomies[ $taxonomy ] ) || ! $taxonomies[ $taxonomy ]->public || ! $taxonomies[ $taxonomy ]->hierarchical ) { continue; } if ( $term_id && term_exists( $term_id, $taxonomy ) ) { wp_set_object_terms( $post_id, array( $term_id ), $taxonomy ); } } } if ( isset( $_POST['postfee_tags'] ) && isset( $taxonomies['post_tag'] ) ) { $tags = array_filter( array_map( 'trim', explode( ',', sanitize_text_field( wp_unslash( $_POST['postfee_tags'] ) ) ) ) ); wp_set_object_terms( $post_id, array_slice( $tags, 0, 10 ), 'post_tag' ); } } private static function save_image( $post_id ) { if ( empty( $_FILES['postfee_image']['name'] ) ) { return; } require_once ABSPATH . 'wp-admin/includes/file.php'; require_once ABSPATH . 'wp-admin/includes/image.php'; require_once ABSPATH . 'wp-admin/includes/media.php'; $attachment_id = media_handle_upload( 'postfee_image', $post_id, array(), array( 'test_form' => false, 'mimes' => array( 'jpg|jpeg|jpe' => 'image/jpeg', 'png' => 'image/png', 'gif' => 'image/gif', 'webp' => 'image/webp', ), ) ); if ( ! is_wp_error( $attachment_id ) ) { set_post_thumbnail( $post_id, $attachment_id ); } } /** * Moves the user's own post to the trash, where an admin can still * restore it. Posts::on_delete() gives back the post it used. */ private static function delete_post() { $post = self::editable_post( isset( $_POST['postfee_post'] ) ? absint( $_POST['postfee_post'] ) : 0 ); self::to_dashboard( $post && wp_trash_post( $post->ID ) ? 'deleted' : 'error' ); } private static function use_credit() { $post = self::payable_post( isset( $_POST['postfee_post'] ) ? absint( $_POST['postfee_post'] ) : 0 ); if ( $post && Fulfillment::try_credit( $post->ID ) ) { self::to_dashboard( 'credit_used' ); } self::to_dashboard( 'error' ); } private static function checkout() { $plan = Plans::get( isset( $_POST['postfee_plan'] ) ? absint( $_POST['postfee_plan'] ) : 0 ); $post_id = isset( $_POST['postfee_post'] ) ? absint( $_POST['postfee_post'] ) : 0; $user_id = get_current_user_id(); if ( ! $plan ) { self::to_dashboard( 'error' ); } $post = null; if ( 'upgrade' === $plan['type'] ) { $post = self::upgradable_post( $post_id ); if ( ! $post ) { self::to_dashboard( 'error' ); } } elseif ( $post_id ) { $post = self::payable_post( $post_id ); if ( ! $post ) { self::to_dashboard( 'error' ); } } if ( $post && $plan['post_types'] && ! in_array( $post->post_type, $plan['post_types'], true ) ) { self::to_dashboard( 'error' ); } if ( 'subscription' === $plan['type'] ) { foreach ( Entitlements::current_for_user( $user_id ) as $entitlement ) { if ( (int) $entitlement->plan_id === $plan['id'] ) { self::to_dashboard( 'already_subscribed' ); } } } $amount = postfee_to_minor( $plan['price'] ); $gateways = Gateways::for_plan( $plan ); $chosen = isset( $_POST['postfee_gateway'] ) ? sanitize_key( $_POST['postfee_gateway'] ) : ''; $gateway = isset( $gateways[ $chosen ] ) ? $gateways[ $chosen ] : null; if ( $amount > 0 && ! $gateway ) { self::to_dashboard( 'error' ); } $order_id = Orders::create( array( 'user_id' => $user_id, 'plan_id' => $plan['id'], 'post_id' => $post ? $post->ID : 0, 'plan_type' => $plan['type'], 'description' => $post ? $plan['name'] . ': ' . wp_strip_all_tags( $post->post_title ) : $plan['name'], 'amount' => $amount, 'currency' => postfee_currency(), 'gateway' => $amount > 0 ? $gateway->id() : 'free', ) ); if ( ! $order_id ) { self::to_dashboard( 'error' ); } if ( $amount <= 0 ) { Fulfillment::order_paid( $order_id ); self::to_dashboard( 'paid' ); } $url = $gateway->start_checkout( Orders::get( $order_id ), $plan ); if ( is_wp_error( $url ) ) { // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- The reason a payment could not start is only known here. error_log( 'Post Fee: ' . $gateway->id() . ' checkout failed for order ' . $order_id . ': ' . $url->get_error_message() ); self::to_dashboard( 'gateway_error' ); } // The hosted checkout lives on another domain. wp_redirect( $url ); // phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect exit; } private static function cancel_subscription() { $entitlement = Entitlements::get( isset( $_POST['postfee_entitlement'] ) ? absint( $_POST['postfee_entitlement'] ) : 0 ); if ( ! $entitlement || (int) $entitlement->user_id !== get_current_user_id() || 'active' !== $entitlement->status || ! $entitlement->gateway_sub_id ) { self::to_dashboard( 'error' ); } $gateway = Gateways::get( $entitlement->gateway ); $result = $gateway ? $gateway->cancel_subscription( $entitlement ) : false; self::to_dashboard( true === $result ? 'sub_cancelled' : 'error' ); } // phpcs:enable WordPress.Security.NonceVerification.Missing /** * The user coming back from a hosted checkout. Nothing in the URL is * trusted: the gateway is asked whether the order was paid. */ private static function handle_return( $gateway_id ) { // phpcs:disable WordPress.Security.NonceVerification.Recommended $gateway = Gateways::get( $gateway_id ); $order = Orders::get( isset( $_GET['postfee_order'] ) ? absint( $_GET['postfee_order'] ) : 0 ); if ( ! $gateway || ! method_exists( $gateway, 'handle_return' ) || ! $order || $order->gateway !== $gateway->id() || (int) $order->user_id !== get_current_user_id() ) { return; } $session_id = isset( $_GET['session_id'] ) ? sanitize_text_field( wp_unslash( $_GET['session_id'] ) ) : ''; // phpcs:enable WordPress.Security.NonceVerification.Recommended $paid = $gateway->handle_return( $order, $session_id ); self::to_dashboard( $paid ? 'paid' : 'processing' ); } }