'POST', 'callback' => array( __CLASS__, 'handle_stripe' ), 'permission_callback' => '__return_true', // phpcs:ignore WordPress.NamingConventions.ValidHookName -- external webhook, authenticated by signature (see docblock above). ) ); register_rest_route( self::NAMESPACE_, '/webhooks/paypal', array( 'methods' => 'POST', 'callback' => array( __CLASS__, 'handle_paypal' ), 'permission_callback' => '__return_true', ) ); } /** * Handles a Stripe webhook. * * @param WP_REST_Request $request REST request. * @return WP_REST_Response */ public static function handle_stripe( WP_REST_Request $request ) { $service = new VEFMB_Payment_Service(); $result = $service->handle_stripe_webhook( $request->get_body(), $request->get_header( 'stripe-signature' ) ); if ( is_wp_error( $result ) ) { // Always 400 on an invalid signature — never 401/403, which // would wrongly invite sending different credentials, // nor 500, which would suggest a transient server problem. return new WP_REST_Response( array( 'error' => $result->get_error_message() ), 400 ); } return new WP_REST_Response( array( 'received' => true ), 200 ); } /** * Handles a PayPal webhook. * * @param WP_REST_Request $request REST request. * @return WP_REST_Response */ public static function handle_paypal( WP_REST_Request $request ) { $service = new VEFMB_Payment_Service(); $headers = array(); foreach ( $request->get_headers() as $key => $values ) { $headers[ strtolower( $key ) ] = is_array( $values ) ? reset( $values ) : $values; } $result = $service->handle_paypal_webhook( $headers, $request->get_body() ); if ( is_wp_error( $result ) ) { return new WP_REST_Response( array( 'error' => $result->get_error_message() ), 400 ); } return new WP_REST_Response( array( 'received' => true ), 200 ); } }