Initial commit
diff --git a/includes/admin/class-wc-rest-stripe-account-keys-controller.php b/includes/admin/class-wc-rest-stripe-account-keys-controller.php
new file mode 100644
index 0000000..8b6e086
--- /dev/null
+++ b/includes/admin/class-wc-rest-stripe-account-keys-controller.php
@@ -0,0 +1,232 @@
+<?php
+/**
+ * Class WC_REST_Stripe_Account_Keys_Controller
+ */
+
+defined( 'ABSPATH' ) || exit;
+
+/**
+ * REST controller for saving Stripe's test/live account keys.
+ *
+ * This includes Live Publishable Key, Live Secret Key, Webhook Secret.
+ *
+ * @since 5.6.0
+ */
+class WC_REST_Stripe_Account_Keys_Controller extends WC_Stripe_REST_Base_Controller {
+	const STRIPE_GATEWAY_SETTINGS_OPTION_NAME = 'woocommerce_stripe_settings';
+
+	/**
+	 * Endpoint path.
+	 *
+	 * @var string
+	 */
+	protected $rest_base = 'wc_stripe/account_keys';
+
+	/**
+	 * The instance of the Stripe account.
+	 *
+	 * @var WC_Stripe_Account
+	 */
+	private $account;
+
+	/**
+	 * Constructor.
+	 *
+	 * @param WC_Stripe_Account $account The instance of the Stripe account.
+	 */
+	public function __construct( WC_Stripe_Account $account ) {
+		$this->account = $account;
+	}
+
+	/**
+	 * Configure REST API routes.
+	 */
+	public function register_routes() {
+		register_rest_route(
+			$this->namespace,
+			'/' . $this->rest_base,
+			[
+				'methods'             => WP_REST_Server::READABLE,
+				'callback'            => [ $this, 'get_account_keys' ],
+				'permission_callback' => [ $this, 'check_permission' ],
+			]
+		);
+		register_rest_route(
+			$this->namespace,
+			'/' . $this->rest_base,
+			[
+				'methods'             => WP_REST_Server::EDITABLE,
+				'callback'            => [ $this, 'set_account_keys' ],
+				'permission_callback' => [ $this, 'check_permission' ],
+				'args'                => [
+					'publishable_key'      => [
+						'description'       => __( 'Your Stripe API Publishable key, obtained from your Stripe dashboard.', 'woocommerce-gateway-stripe' ),
+						'type'              => 'string',
+						'validate_callback' => [ $this, 'validate_publishable_key' ],
+					],
+					'secret_key'           => [
+						'description'       => __( 'Your Stripe API Secret, obtained from your Stripe dashboard.', 'woocommerce-gateway-stripe' ),
+						'type'              => 'string',
+						'validate_callback' => [ $this, 'validate_secret_key' ],
+					],
+					'webhook_secret'       => [
+						'description'       => __( 'Your Stripe webhook endpoint URL, obtained from your Stripe dashboard.', 'woocommerce-gateway-stripe' ),
+						'type'              => 'string',
+						'validate_callback' => 'rest_validate_request_arg',
+					],
+					'test_publishable_key' => [
+						'description'       => __( 'Your Stripe testing API Publishable key, obtained from your Stripe dashboard.', 'woocommerce-gateway-stripe' ),
+						'type'              => 'string',
+						'validate_callback' => [ $this, 'validate_test_publishable_key' ],
+					],
+					'test_secret_key'      => [
+						'description'       => __( 'Your Stripe testing API Secret, obtained from your Stripe dashboard.', 'woocommerce-gateway-stripe' ),
+						'type'              => 'string',
+						'validate_callback' => [ $this, 'validate_test_secret_key' ],
+					],
+					'test_webhook_secret'  => [
+						'description'       => __( 'Your Stripe testing webhook endpoint URL, obtained from your Stripe dashboard.', 'woocommerce-gateway-stripe' ),
+						'type'              => 'string',
+						'validate_callback' => 'rest_validate_request_arg',
+					],
+				],
+			]
+		);
+	}
+
+	/**
+	 * Retrieve flag status.
+	 *
+	 * @return WP_REST_Response
+	 */
+	public function get_account_keys() {
+		$allowed_params  = [ 'publishable_key', 'secret_key', 'webhook_secret', 'test_publishable_key', 'test_secret_key', 'test_webhook_secret' ];
+		$stripe_settings = get_option( self::STRIPE_GATEWAY_SETTINGS_OPTION_NAME, [] );
+		// Filter only the fields we want to return
+		$account_keys = array_intersect_key( $stripe_settings, array_flip( $allowed_params ) );
+
+		return new WP_REST_Response( $account_keys );
+	}
+
+	/**
+	 * Validate stripe publishable keys and secrets. Allow empty string to erase key.
+	 * Also validates against explicit key prefixes based on live/test environment.
+	 *
+	 * @param mixed           $value
+	 * @param WP_REST_Request $request
+	 * @param string          $param
+	 * @param array $validate_options
+	 * @return true|WP_Error
+	 */
+	private function validate_stripe_param( $param, $request, $key, $validate_options ) {
+		if ( empty( $param ) ) {
+			return true;
+		}
+		$result = rest_validate_request_arg( $param, $request, $key );
+		if ( ! empty( $result ) && ! preg_match( $validate_options['regex'], $param ) ) {
+			return new WP_Error( 400, $validate_options['error_message'] );
+		}
+		return true;
+	}
+
+	public function validate_publishable_key( $param, $request, $key ) {
+		return $this->validate_stripe_param(
+			$param,
+			$request,
+			$key,
+			[
+				'regex'         => '/^pk_live_/',
+				'error_message' => __( 'The "Live Publishable Key" should start with "pk_live", enter the correct key.', 'woocommerce-gateway-stripe' ),
+			]
+		);
+	}
+
+	public function validate_secret_key( $param, $request, $key ) {
+		return $this->validate_stripe_param(
+			$param,
+			$request,
+			$key,
+			[
+				'regex'         => '/^[rs]k_live_/',
+				'error_message' => __( 'The "Live Secret Key" should start with "sk_live" or "rk_live", enter the correct key.', 'woocommerce-gateway-stripe' ),
+			]
+		);
+	}
+
+	public function validate_test_publishable_key( $param, $request, $key ) {
+		return $this->validate_stripe_param(
+			$param,
+			$request,
+			$key,
+			[
+				'regex'         => '/^pk_test_/',
+				'error_message' => __( 'The "Test Publishable Key" should start with "pk_test", enter the correct key.', 'woocommerce-gateway-stripe' ),
+			]
+		);
+	}
+
+	public function validate_test_secret_key( $param, $request, $key ) {
+		return $this->validate_stripe_param(
+			$param,
+			$request,
+			$key,
+			[
+				'regex'         => '/^[rs]k_test_/',
+				'error_message' => __( 'The "Test Secret Key" should start with "sk_test" or "rk_test", enter the correct key.', 'woocommerce-gateway-stripe' ),
+			]
+		);
+	}
+
+	/**
+	 * Update the data.
+	 *
+	 * @param WP_REST_Request $request Full data about the request.
+	 */
+	public function set_account_keys( WP_REST_Request $request ) {
+		$publishable_key      = $request->get_param( 'publishable_key' );
+		$secret_key           = $request->get_param( 'secret_key' );
+		$webhook_secret       = $request->get_param( 'webhook_secret' );
+		$test_publishable_key = $request->get_param( 'test_publishable_key' );
+		$test_secret_key      = $request->get_param( 'test_secret_key' );
+		$test_webhook_secret  = $request->get_param( 'test_webhook_secret' );
+
+		$settings = get_option( self::STRIPE_GATEWAY_SETTINGS_OPTION_NAME, [] );
+
+		// If all keys were empty, then is a new account; we need to set the test/live mode.
+		$new_account = ! trim( $settings['publishable_key'] )
+					&& ! trim( $settings['secret_key'] )
+					&& ! trim( $settings['test_publishable_key'] )
+					&& ! trim( $settings['test_secret_key'] );
+		// If all new keys are empty, then account is being disconnected. We should disable the payment gateway.
+		$is_deleting_account = ! trim( $publishable_key )
+							&& ! trim( $secret_key )
+							&& ! trim( $test_publishable_key )
+							&& ! trim( $test_secret_key );
+
+		$settings['publishable_key']      = is_null( $publishable_key ) ? $settings['publishable_key'] : $publishable_key;
+		$settings['secret_key']           = is_null( $secret_key ) ? $settings['secret_key'] : $secret_key;
+		$settings['webhook_secret']       = is_null( $webhook_secret ) ? $settings['webhook_secret'] : $webhook_secret;
+		$settings['test_publishable_key'] = is_null( $test_publishable_key ) ? $settings['test_publishable_key'] : $test_publishable_key;
+		$settings['test_secret_key']      = is_null( $test_secret_key ) ? $settings['test_secret_key'] : $test_secret_key;
+		$settings['test_webhook_secret']  = is_null( $test_webhook_secret ) ? $settings['test_webhook_secret'] : $test_webhook_secret;
+
+		if ( $new_account ) {
+			$settings['enabled'] = 'yes';
+			if ( trim( $settings['publishable_key'] ) && trim( $settings['secret_key'] ) ) {
+				$settings['testmode'] = 'no';
+			} elseif ( trim( $settings['test_publishable_key'] ) && trim( $settings['test_secret_key'] ) ) {
+				$settings['testmode'] = 'yes';
+			}
+		} elseif ( $is_deleting_account ) {
+			$settings['enabled'] = 'no';
+		}
+
+		update_option( self::STRIPE_GATEWAY_SETTINGS_OPTION_NAME, $settings );
+		$this->account->clear_cache();
+
+		// Gives an instant reply if the connection was succesful or not + rebuild the cache for the next request
+		$account = $this->account->get_cached_account_data();
+
+		return new WP_REST_Response( $account, 200 );
+	}
+}