400 ) ); } $valid_data = array( 'gateway' => 'paypal_commerce', 'discount' => $discount, 'cc_info' => array(), 'need_new_user' => false, 'need_user_login' => false, 'logged_in_user' => array(), 'new_user_data' => array(), 'login_user_data' => array(), 'guest_user_data' => array(), ); $purchase_var = ! empty( $form_data['edd-purchase-var'] ) ? sanitize_text_field( $form_data['edd-purchase-var'] ) : ''; if ( is_user_logged_in() ) { $valid_data['logged_in_user'] = array( 'user_id' => get_current_user_id(), 'user_email' => $email, 'user_first' => $first_name, 'user_last' => $last_name, ); } elseif ( 'needs-to-register' === $purchase_var ) { // Populate $_POST so edd_purchase_form_validate_new_user() can read // the registration fields; password fields are not sanitized to // preserve the buyer's chosen characters. $_POST['edd_email'] = $email; $_POST['edd_first'] = $first_name; $_POST['edd_last'] = $last_name; $_POST['edd-purchase-var'] = 'needs-to-register'; $_POST['edd_user_login'] = ! empty( $form_data['edd_user_login'] ) ? sanitize_user( $form_data['edd_user_login'] ) : ''; $_POST['edd_user_pass'] = $form_data['edd_user_pass'] ?? ''; // Honor an explicit confirm field; fall back to the password itself // so single-password-field themes don't fail the mismatch check. $_POST['edd_user_pass_confirm'] = ! empty( $form_data['edd_user_pass_confirm'] ) ? $form_data['edd_user_pass_confirm'] : $_POST['edd_user_pass']; $valid_data['need_new_user'] = true; $valid_data['new_user_data'] = edd_purchase_form_validate_new_user(); $errors = edd_get_errors(); if ( $errors ) { edd_clear_errors(); return new \WP_Error( 'registration_failed', reset( $errors ), array( 'status' => 400 ) ); } } elseif ( 'needs-to-login' === $purchase_var ) { // sanitize_text_field rather than sanitize_user because the login field // accepts an email address, which sanitize_user would mangle. $user_login = ! empty( $form_data['edd_user_login'] ) ? sanitize_text_field( $form_data['edd_user_login'] ) : ''; $user_pass = $form_data['edd_user_pass'] ?? ''; $wp_user = edd_log_user_in( 0, $user_login, $user_pass, false ); $errors = edd_get_errors(); if ( $errors ) { edd_clear_errors(); return new \WP_Error( 'login_failed', reset( $errors ), array( 'status' => 400 ) ); } // edd_log_user_in() calls wp_set_current_user(), so is_user_logged_in() // is now true. Populate logged_in_user so edd_get_purchase_form_user() // resolves correctly through the logged-in branch. $valid_data['logged_in_user'] = array( 'user_id' => $wp_user->ID, 'user_email' => $wp_user->user_email, 'user_first' => $wp_user->first_name, 'user_last' => $wp_user->last_name, ); } else { // Guest checkout — enforce the site's guest policy. if ( edd_no_guest_checkout() ) { return new \WP_Error( 'registration_required', __( 'You must register or login to complete your purchase.', 'easy-digital-downloads' ), array( 'status' => 400 ) ); } $valid_data['guest_user_data'] = array( 'user_id' => 0, 'user_email' => $email, 'user_first' => $first_name, 'user_last' => $last_name, ); } $user = edd_get_purchase_form_user( $valid_data, false ); /** * Let extensions validate fields after the user has been resolved. * * Fired here so a REST checkout is held to the same user checks a form * submission is, including the check that refuses a guest submitting a * registered account's email address. * * @since 3.7.1 * * @param array|bool $user The resolved user data. * @param array $valid_data The valid data. * @param array $form_data The submitted form data. */ do_action( 'edd_checkout_user_error_checks', $user, $valid_data, $form_data ); $errors = edd_get_errors(); if ( empty( $user ) || $errors ) { edd_clear_errors(); $message = $errors ? reset( $errors ) : __( 'An error occurred with your account. Please try again.', 'easy-digital-downloads' ); return new \WP_Error( 'user_error', $message, array( 'status' => 400 ) ); } $user['address'] = $address; return array( 'valid_data' => $valid_data, 'user' => $user, ); } /** * Issues an auth cookie after capture if the order's buyer just registered * or logged in during this request. * * The cookie set during the /create response may be stripped by proxy * layers (e.g. Cloudflare APO); repeating it on the capture response — * which is the one the browser navigates away from — ensures the buyer * lands on the success page logged in. * * Deliberate side effect: for proxy-stripped buyers wp_signon() already * fires wp_login server-side during create_order(); this call fires it a * second time on capture. Duplicate audit-log entries and last-login stamps * are the trade-off for reliable session delivery in that scenario. * * @since 3.6.9 * * @param int $order_id EDD order ID. * @return void */ public static function maybe_log_in_after_capture( $order_id ) { if ( is_user_logged_in() ) { return; } $order = edd_get_order( $order_id ); if ( ! $order || empty( $order->email ) ) { return; } $wp_user = get_user_by( 'email', $order->email ); if ( $wp_user && (int) $order->user_id === (int) $wp_user->ID ) { wp_set_auth_cookie( $wp_user->ID ); do_action( 'wp_login', $wp_user->user_login, $wp_user ); } } }