array( 'type' => 'string', 'default' => '', 'sanitize_callback' => 'sanitize_text_field', ), 'per_page' => array( 'type' => 'integer', 'default' => 20, 'minimum' => 1, 'maximum' => 100, ), ); $this->route( '/options/products', array( 'methods' => \WP_REST_Server::READABLE, 'callback' => array( $this, 'products' ), 'args' => $search, ) ); $this->route( '/options/variations', array( 'methods' => \WP_REST_Server::READABLE, 'callback' => array( $this, 'variations' ), 'args' => array( 'product_id' => $this->int_param( true ) ), ) ); $this->route( '/options/gateways', array( 'methods' => \WP_REST_Server::READABLE, 'callback' => array( $this, 'gateways' ), ) ); $this->route( '/options/currencies', array( 'methods' => \WP_REST_Server::READABLE, 'callback' => array( $this, 'currencies' ), ) ); $this->route( '/options/tax-classes', array( 'methods' => \WP_REST_Server::READABLE, 'callback' => array( $this, 'tax_classes' ), ) ); $this->route( '/options/statuses', array( 'methods' => \WP_REST_Server::READABLE, 'callback' => array( $this, 'statuses' ), ) ); $this->route( '/options/billing-periods', array( 'methods' => \WP_REST_Server::READABLE, 'callback' => array( $this, 'billing_periods' ), ) ); $this->route( '/options/customers', array( 'methods' => \WP_REST_Server::READABLE, 'callback' => array( $this, 'customers' ), 'args' => $search, ) ); } /** * Subscription products. * * @param \WP_REST_Request $request Request. * @return \WP_REST_Response */ public function products( \WP_REST_Request $request ) { $products = wc_get_products( array( 'type' => array( 'subscription', 'variable-subscription' ), 'status' => array( 'publish', 'private', 'draft', 'pending' ), 's' => $request['search'], 'limit' => (int) $request['per_page'], 'orderby' => 'title', 'order' => 'ASC', 'return' => 'objects', ) ); $out = array(); foreach ( $products as $product ) { $out[] = array( 'id' => $product->get_id(), 'name' => $product->get_name(), 'sku' => (string) $product->get_sku(), 'type' => $product->get_type(), ); } return $this->respond( $out ); } /** * Variations of a variable subscription product. * * @param \WP_REST_Request $request Request. * @return \WP_REST_Response */ public function variations( \WP_REST_Request $request ) { $variations = wc_get_products( array( // Variations have no product type term, so the query matches the post type: 'variation'. 'type' => 'variation', 'parent' => (int) $request['product_id'], 'status' => array( 'publish', 'private' ), 'limit' => -1, 'return' => 'objects', ) ); $out = array(); foreach ( $variations as $variation ) { $out[] = array( 'id' => $variation->get_id(), 'name' => $variation->get_name(), 'sku' => (string) $variation->get_sku(), ); } return $this->respond( $out ); } /** * Payment gateways (including disabled ones: old subscriptions may still reference them). * * @return \WP_REST_Response */ public function gateways() { $out = array( array( 'id' => '', 'title' => __( 'None (manual renewal)', 'recast-subscriptions' ), 'enabled' => true, 'supports_subscriptions' => false, ), ); $gateways = WC()->payment_gateways()->payment_gateways(); foreach ( $gateways as $id => $gateway ) { $out[] = array( 'id' => (string) $id, 'title' => $gateway->get_method_title(), 'enabled' => 'yes' === $gateway->enabled, 'supports_subscriptions' => (bool) $gateway->supports( 'subscriptions' ), ); } // Gateways referenced by existing subscriptions but no longer installed. foreach ( self::gateways_in_use() as $id ) { if ( '' !== $id && ! isset( $gateways[ $id ] ) ) { $out[] = array( 'id' => $id, 'title' => sprintf( /* translators: %s: payment method name or ID */ __( '%s (not installed)', 'recast-subscriptions' ), $id ), 'enabled' => false, 'supports_subscriptions' => false, ); } } return $this->respond( $out ); } /** * Distinct payment method IDs on existing subscriptions (cached hourly). * * @return string[] */ private static function gateways_in_use() { $cached = get_transient( 'recast_gateways_in_use' ); if ( is_array( $cached ) ) { return $cached; } $ids = array(); $subs = wcs_get_subscriptions( array( 'subscriptions_per_page' => 500, 'subscription_status' => 'any', 'orderby' => 'ID', 'order' => 'DESC', ) ); foreach ( $subs as $sub ) { $ids[ (string) $sub->get_payment_method() ] = 1; } $ids = array_keys( $ids ); set_transient( 'recast_gateways_in_use', $ids, HOUR_IN_SECONDS ); return $ids; } /** * Currencies, store currency first. * * @return \WP_REST_Response */ public function currencies() { $store = get_woocommerce_currency(); $in_use = self::currencies_in_use(); $out = array(); foreach ( get_woocommerce_currencies() as $code => $name ) { $row = array( 'code' => $code, 'name' => html_entity_decode( $name ), 'symbol' => html_entity_decode( get_woocommerce_currency_symbol( $code ) ), 'in_use' => in_array( $code, $in_use, true ), ); if ( $code === $store ) { array_unshift( $out, $row ); } else { $out[] = $row; } } return $this->respond( $out ); } /** * Currencies used by existing subscriptions (the 2,000 newest, cached hourly), so a change * by an amount can warn when a selection can hold several currencies. * * @return string[] */ private static function currencies_in_use() { $cached = get_transient( 'recast_currencies_in_use' ); if ( is_array( $cached ) ) { return $cached; } $codes = array(); $subs = wcs_get_subscriptions( array( 'subscriptions_per_page' => 2000, 'subscription_status' => 'any', 'orderby' => 'ID', 'order' => 'DESC', ) ); foreach ( $subs as $sub ) { $codes[ (string) $sub->get_currency() ] = 1; } $codes = array_keys( $codes ); set_transient( 'recast_currencies_in_use', $codes, HOUR_IN_SECONDS ); return $codes; } /** * Tax classes including Standard. * * @return \WP_REST_Response */ public function tax_classes() { $out = array( array( 'slug' => '', 'name' => __( 'Standard', 'recast-subscriptions' ), ), ); foreach ( \WC_Tax::get_tax_classes() as $name ) { $out[] = array( 'slug' => sanitize_title( $name ), 'name' => $name, ); } return $this->respond( $out ); } /** * Subscription statuses, unprefixed. * * @return \WP_REST_Response */ public function statuses() { $out = array(); foreach ( wcs_get_subscription_statuses() as $slug => $label ) { $out[] = array( 'slug' => preg_replace( '/^wc-/', '', (string) $slug ), 'label' => $label, ); } return $this->respond( $out ); } /** * Billing periods and intervals. * * @return \WP_REST_Response */ public function billing_periods() { $periods = array(); foreach ( wcs_get_subscription_period_strings() as $period => $label ) { $periods[] = array( 'period' => (string) $period, 'label' => $label, ); } $intervals = array(); foreach ( wcs_get_subscription_period_interval_strings() as $interval => $label ) { $intervals[] = array( 'interval' => (int) $interval, 'label' => $label, ); } return $this->respond( array( 'periods' => $periods, 'intervals' => $intervals, ) ); } /** * Customer search. Only users with a customer role show, so staff and administrator emails do * not appear in the cohort builder. * * @param \WP_REST_Request $request Request. * @return \WP_REST_Response */ public function customers( \WP_REST_Request $request ) { $search = (string) $request['search']; $query = new \WP_User_Query( array( 'role__in' => self::customer_roles(), 'search' => '' === $search ? '' : '*' . $search . '*', 'search_columns' => array( 'user_email', 'user_login', 'display_name', 'user_nicename' ), 'number' => (int) $request['per_page'], 'orderby' => 'display_name', 'fields' => array( 'ID', 'user_email', 'display_name' ), ) ); $out = array(); foreach ( $query->get_results() as $user ) { $out[] = array( 'id' => (int) $user->ID, 'email' => $user->user_email, 'name' => $user->display_name, ); } return $this->respond( $out ); } /** * Roles that customers have: the WooCommerce customer role and the roles that WooCommerce * Subscriptions gives to active and inactive subscribers. * * @return string[] */ private static function customer_roles() { $roles = array( 'customer', (string) get_option( 'woocommerce_subscriptions_subscriber_role', 'subscriber' ), (string) get_option( 'woocommerce_subscriptions_cancelled_role', 'customer' ), ); /** * Filters the roles whose users show in the customer search. * * @param string[] $roles Role slugs. */ $roles = (array) apply_filters( 'recast_customer_roles', array_values( array_unique( array_filter( $roles ) ) ) ); return $roles ? $roles : array( 'customer' ); } }