10, 'redirection' => 0, 'headers' => array( 'Content-Type' => 'application/json' ), 'body' => wp_json_encode( $body ), ) ); if ( is_wp_error( $response ) || 200 !== (int) wp_remote_retrieve_response_code( $response ) ) { return null; } $decoded = json_decode( wp_remote_retrieve_body( $response ), true ); return is_array( $decoded ) ? $decoded : null; } /** * Starts pairing and redirects the admin to the backend approval page. */ function orderping_handle_connect() { if ( ! current_user_can( 'manage_woocommerce' ) ) { wp_die( esc_html__( 'You do not have permission to do this.', 'commerceping' ), 403 ); } check_admin_referer( 'orderping_connect' ); // Explicit opt-in: nothing is sent to CommercePing until the admin ticks the consent box. if ( empty( $_POST['orderping_consent'] ) ) { orderping_redirect_to_settings( 'consent_required' ); } $state = bin2hex( random_bytes( 16 ) ); // Built with add_query_arg: wp_nonce_url() HTML-escapes the ampersand. $return = add_query_arg( '_wpnonce', wp_create_nonce( 'orderping_complete' ), admin_url( 'admin-post.php?action=orderping_complete' ) ); $result = orderping_pairing_request( '/woo/pair/start', array( 'site_url' => untrailingslashit( home_url() ), 'instance_id' => orderping_instance_id(), 'state' => $state, 'return_url' => $return, ) ); $approve = is_array( $result ) && isset( $result['approve_url'], $result['code'] ) ? esc_url_raw( (string) $result['approve_url'] ) : ''; // Only ever send the admin to the configured backend host. if ( '' === $approve || wp_parse_url( $approve, PHP_URL_HOST ) !== wp_parse_url( orderping_backend_url(), PHP_URL_HOST ) ) { orderping_redirect_to_settings( 'start_failed' ); } update_option( ORDERPING_OPTION_PAIRING, array( 'code' => (string) $result['code'], 'state' => $state, ), false ); // phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect -- external, host-checked above. wp_redirect( $approve ); exit; } /** * Exchanges the approved code for the per-store secret. */ function orderping_handle_complete() { if ( ! current_user_can( 'manage_woocommerce' ) ) { wp_die( esc_html__( 'You do not have permission to do this.', 'commerceping' ), 403 ); } check_admin_referer( 'orderping_complete' ); $pending = get_option( ORDERPING_OPTION_PAIRING ); // A completion link is single use: drop the pending pairing before the exchange. delete_option( ORDERPING_OPTION_PAIRING ); if ( ! is_array( $pending ) || empty( $pending['code'] ) || empty( $pending['state'] ) ) { orderping_redirect_to_settings( 'no_pairing' ); } $result = orderping_pairing_request( '/woo/pair/complete', array( 'code' => (string) $pending['code'], 'instance_id' => orderping_instance_id(), 'state' => (string) $pending['state'], ) ); if ( ! is_array( $result ) || empty( $result['connection_id'] ) || empty( $result['secret'] ) || (int) ( $result['contract_version'] ?? 0 ) !== ORDERPING_CONTRACT_VERSION ) { orderping_redirect_to_settings( 'complete_failed' ); } // A new pairing never inherits queued work from an earlier one. orderping_discard_queued_work(); update_option( ORDERPING_OPTION_CONNECTION, array( 'id' => sanitize_text_field( (string) $result['connection_id'] ), 'secret' => (string) $result['secret'], 'epoch' => (int) ( $result['connection_epoch'] ?? 1 ), 'connected_at' => time(), ), false ); // Display-only; taken solely from the completion response, never invented. Absent means no email shown. $email = isset( $result['account_email'] ) && is_string( $result['account_email'] ) ? sanitize_email( $result['account_email'] ) : ''; if ( '' !== $email ) { update_option( ORDERPING_OPTION_ACCOUNT_EMAIL, $email, false ); } else { delete_option( ORDERPING_OPTION_ACCOUNT_EMAIL ); } orderping_redirect_to_settings( 'connected' ); } /** * Sends a signed ping so the admin can confirm the channel works. */ function orderping_handle_test_ping() { if ( ! current_user_can( 'manage_woocommerce' ) ) { wp_die( esc_html__( 'You do not have permission to do this.', 'commerceping' ), 403 ); } check_admin_referer( 'orderping_test_ping' ); $result = orderping_send_event( array( 'type' => 'ping', 'event_id' => bin2hex( random_bytes( 8 ) ), 'contract_version' => ORDERPING_CONTRACT_VERSION, 'plugin_version' => ORDERPING_VERSION, ) ); orderping_redirect_to_settings( $result['ok'] ? 'ping_ok' : 'ping_failed' ); } /** * Redirects to the settings page with a status code, then exits. * * @param string $notice Notice code. */ function orderping_redirect_to_settings( $notice ) { wp_safe_redirect( add_query_arg( 'orderping_notice', rawurlencode( $notice ), admin_url( 'admin.php?page=orderping' ) ) ); exit; }