mirror of
https://github.com/wp-graphql/wp-graphql-woocommerce.git
synced 2026-08-14 12:53:44 +02:00
* fix: address WordPress.org plugin review feedback - Prefix the session transaction queue transient with the plugin's graphql_woocommerce_ namespace instead of the generic "woo_" word, to avoid collisions (Plugin Directory: prefix data storage). - Declare the WooCommerce dependency via the "Requires Plugins: woocommerce" plugin header. - Bump README.txt "Tested up to" to 7.0. - Ship composer.json in the distributed plugin (drop it, and composer.lock, from composer archive excludes) so the build is reproducible/reviewable. * chore: rename plugin to "GraphQL for eCommerce" for trademark compliance The WordPress.org plugin review flagged the display name/slug for beginning with the "WPGraphQL" trademark (and the "WooGraphQL" portmanteau of the WooCommerce mark), which can imply official affiliation. - Display name (plugin header + readme title) -> "GraphQL for eCommerce". - Slug/text domain -> "graphql-for-ecommerce" (header, all i18n string literals, and the PHPCS WordPress.WP.I18n text_domain config). - Update user-facing notices/errors that named the old plugin. "WooGraphQL" remains the project's informal nickname (repo, docs, community), just not in the WordPress.org directory's official name/slug. Internal file names and GitHub URLs are unchanged. * fix: keep test-only dev deps out of the committed composer.json The committed manifest mirrors develop (lint/stan dev deps only); CI adds the test suite deps at runtime via `composer installTestEnv`. A previous commit captured the installTestEnv-modified composer.json, desyncing it from composer.lock and breaking `composer install` in CI. * chore: regenerate composer.lock (refresh dev dependencies) Regenerate the lock from the manifest so it is in sync (fixes the CI `composer install` failure) and refresh dependencies in the process — firebase/php-jwt v7.0.4 -> v7.1.0 plus 11 others, with vendor-prefixed re-strauss'd to match. Full wpunit suite passes against the updated deps (305 tests, 835 assertions). * chore: rename text domain in createdVia/attribution strings from #1018 #1018 (createdVia + order attribution) merged into develop after the rename commit was authored, so its new i18n strings still used the old 'wp-graphql-woocommerce' text domain. Update them to 'graphql-for-ecommerce' to match the rename.
391 lines
10 KiB
PHP
391 lines
10 KiB
PHP
<?php
|
|
/**
|
|
* Compatibility integrations with third-party plugins.
|
|
*
|
|
* @package \WPGraphQL\WooCommerce
|
|
* @since 1.0.0
|
|
*/
|
|
|
|
namespace WPGraphQL\WooCommerce;
|
|
|
|
use GraphQL\Error\UserError;
|
|
use WPGraphQL\WooCommerce\Model\Customer;
|
|
|
|
/**
|
|
* Class Compatibility
|
|
*/
|
|
class Compatibility {
|
|
/**
|
|
* Register all compatibility filters.
|
|
*
|
|
* @return void
|
|
*/
|
|
public static function setup() {
|
|
self::register_wc_admin_settings();
|
|
self::add_acf_filters();
|
|
self::add_jwt_auth_filters();
|
|
self::add_stripe_gateway_filters();
|
|
self::add_swp_filters();
|
|
}
|
|
|
|
/**
|
|
* Register WC admin settings for GraphQL requests.
|
|
*
|
|
* WooCommerce only registers settings groups during rest_api_init.
|
|
* We need them available for GraphQL settings queries and mutations.
|
|
*
|
|
* @return void
|
|
*/
|
|
private static function register_wc_admin_settings() {
|
|
if ( method_exists( WC(), 'register_wp_admin_settings' ) ) {
|
|
WC()->register_wp_admin_settings(); // @phpstan-ignore method.private (public since WC 9.0)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Register WPGraphQL ACF compatibility filters.
|
|
*
|
|
* @return void
|
|
*/
|
|
private static function add_acf_filters() {
|
|
add_filter( 'graphql_acf_get_root_id', [ self::class, 'resolve_crud_root_id' ], 10, 2 );
|
|
add_filter( 'graphql_acf_post_object_source', [ self::class, 'resolve_post_object_source' ], 10, 2 );
|
|
}
|
|
|
|
/**
|
|
* Resolve post object ID from CRUD object Model.
|
|
*
|
|
* @param integer|null $id Post object database ID.
|
|
* @param mixed $root Root resolver.
|
|
*
|
|
* @return integer|null
|
|
*/
|
|
public static function resolve_crud_root_id( $id, $root ) {
|
|
if ( $root instanceof Model\WC_Post ) {
|
|
$id = absint( $root->ID );
|
|
}
|
|
|
|
return $id;
|
|
}
|
|
|
|
/**
|
|
* Filters ACF "post_object" field type resolver to ensure that
|
|
* the proper Type source is provided for WooCommerce CPTs.
|
|
*
|
|
* @param mixed|null $source source of the data being provided.
|
|
* @param mixed|null $value Post ID.
|
|
*
|
|
* @return mixed|null
|
|
*/
|
|
public static function resolve_post_object_source( $source, $value ) {
|
|
$post = get_post( $value );
|
|
if ( $post instanceof \WP_Post ) {
|
|
switch ( $post->post_type ) {
|
|
case 'shop_coupon':
|
|
$source = new Model\Coupon( $post->ID );
|
|
break;
|
|
case 'shop_order':
|
|
$source = new Model\Order( $post->ID );
|
|
break;
|
|
case 'product':
|
|
$source = new Model\Product( $post->ID );
|
|
break;
|
|
case 'product_variation':
|
|
$source = new Model\Product_Variation( $post->ID );
|
|
break;
|
|
}
|
|
}
|
|
|
|
return $source;
|
|
}
|
|
|
|
/**
|
|
* Get the JWT auth class if available.
|
|
*
|
|
* @return string|null
|
|
*/
|
|
public static function get_auth_class() {
|
|
if ( class_exists( 'WPGraphQL\JWT_Authentication\Auth' ) ) {
|
|
return \WPGraphQL\JWT_Authentication\Auth::class;
|
|
} elseif ( class_exists( 'WPGraphQL\Login\Auth\TokenManager' ) ) {
|
|
return \WPGraphQL\Login\Auth\TokenManager::class;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Get the auth token for a user.
|
|
*
|
|
* @param \WP_User $user The user object.
|
|
*
|
|
* @throws \GraphQL\Error\UserError If the token cannot be retrieved.
|
|
*
|
|
* @return string|null
|
|
*/
|
|
public static function get_auth_token( \WP_User $user ) {
|
|
$auth_class = self::get_auth_class();
|
|
|
|
if ( ! $auth_class ) {
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* @var \WP_Error|string|null $token
|
|
*/
|
|
$token = null;
|
|
if ( 'WPGraphQL\JWT_Authentication\Auth' === $auth_class ) {
|
|
$token = $auth_class::get_token( $user );
|
|
} elseif ( 'WPGraphQL\Login\Auth\TokenManager' === $auth_class ) {
|
|
$token = $auth_class::get_auth_token( $user );
|
|
}
|
|
|
|
if ( is_wp_error( $token ) ) {
|
|
throw new UserError( $token->get_error_message() );
|
|
}
|
|
|
|
return $token;
|
|
}
|
|
|
|
/**
|
|
* Get the refresh token for a user.
|
|
*
|
|
* @param \WP_User $user The user object.
|
|
*
|
|
* @throws \GraphQL\Error\UserError If the token cannot be retrieved.
|
|
*
|
|
* @return string|null
|
|
*/
|
|
public static function get_refresh_token( \WP_User $user ) {
|
|
$auth_class = self::get_auth_class();
|
|
|
|
if ( ! $auth_class ) {
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* @var \WP_Error|string|null $refresh_token
|
|
*/
|
|
$refresh_token = $auth_class::get_refresh_token( $user );
|
|
|
|
if ( is_wp_error( $refresh_token ) ) {
|
|
throw new UserError( $refresh_token->get_error_message() );
|
|
}
|
|
|
|
return $refresh_token;
|
|
}
|
|
|
|
/**
|
|
* Register WPGraphQL JWT Authentication compatibility filters.
|
|
*
|
|
* @return void
|
|
*/
|
|
private static function add_jwt_auth_filters() {
|
|
$auth_class = self::get_auth_class();
|
|
if ( is_null( $auth_class ) ) {
|
|
return;
|
|
}
|
|
|
|
add_filter( 'graphql_jwt_user_types', [ self::class, 'add_customer_to_jwt_user_types' ], 10 );
|
|
add_filter( 'graphql_registerCustomerPayload_fields', [ self::class, 'add_jwt_output_fields' ], 10, 3 );
|
|
add_filter( 'graphql_updateCustomerPayload_fields', [ self::class, 'add_jwt_output_fields' ], 10, 3 );
|
|
add_action( 'graphql_register_types', [ self::class, 'add_customer_to_login_payload' ], 10 );
|
|
}
|
|
|
|
/**
|
|
* Adds Customer type to the JWT User type list.
|
|
*
|
|
* @param array $types JWT User types.
|
|
* @return array
|
|
*/
|
|
public static function add_customer_to_jwt_user_types( array $types ) {
|
|
$types[] = 'Customer';
|
|
|
|
return $types;
|
|
}
|
|
|
|
/**
|
|
* Adds all JWT related fields to the Customer mutation output.
|
|
*
|
|
* @param array $fields Mutation output field definitions.
|
|
* @param \WPGraphQL\Type\WPInputObjectType $object_type The WPInputObjectType the fields are be added to.
|
|
* @param \WPGraphQL\Registry\TypeRegistry $type_registry TypeRegistry instance.
|
|
*
|
|
* @return array
|
|
*/
|
|
public static function add_jwt_output_fields( $fields, $object_type, $type_registry ): array {
|
|
return array_merge(
|
|
$fields,
|
|
[
|
|
'authToken' => [
|
|
'type' => $type_registry->get_type( 'String' ),
|
|
'description' => static function () {
|
|
return __( 'JWT Token that can be used in future requests for Authentication', 'graphql-for-ecommerce' );
|
|
},
|
|
'resolve' => static function ( $payload ) {
|
|
$user = get_user_by( 'ID', $payload['id'] );
|
|
|
|
if ( ! $user ) {
|
|
throw new UserError( __( 'User not found.', 'graphql-for-ecommerce' ) );
|
|
}
|
|
|
|
return self::get_auth_token( $user );
|
|
},
|
|
],
|
|
'refreshToken' => [
|
|
'type' => $type_registry->get_type( 'String' ),
|
|
'description' => static function () {
|
|
return __( 'A JWT token that can be used in future requests to get a refreshed jwtAuthToken. If the refresh token used in a request is revoked or otherwise invalid, a valid Auth token will NOT be issued in the response headers.', 'graphql-for-ecommerce' );
|
|
},
|
|
'resolve' => static function ( $payload ) {
|
|
$user = get_user_by( 'ID', $payload['id'] );
|
|
|
|
if ( ! $user ) {
|
|
throw new UserError( __( 'User not found.', 'graphql-for-ecommerce' ) );
|
|
}
|
|
|
|
return self::get_refresh_token( $user );
|
|
},
|
|
],
|
|
]
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Adds "customer" field to "login" mutation payload.
|
|
*
|
|
* @return void
|
|
*/
|
|
public static function add_customer_to_login_payload() {
|
|
register_graphql_field(
|
|
'LoginPayload',
|
|
'customer',
|
|
[
|
|
'type' => 'Customer',
|
|
'description' => static function () {
|
|
return __( 'Customer object of authenticated user.', 'graphql-for-ecommerce' );
|
|
},
|
|
'resolve' => static function ( $payload ) {
|
|
$id = $payload['id'];
|
|
return new Customer( $id );
|
|
},
|
|
]
|
|
);
|
|
|
|
if ( ! wc_graphql_is_session_handler_disabled() ) {
|
|
$token_type = woographql_setting( 'set_session_token_type', 'legacy' );
|
|
if ( in_array( $token_type, [ 'legacy', 'both' ], true ) ) {
|
|
register_graphql_field(
|
|
'LoginPayload',
|
|
'sessionToken',
|
|
[
|
|
'type' => 'String',
|
|
'description' => static function () {
|
|
return __( 'A JWT token that can be used in future requests to for WooCommerce session identification', 'graphql-for-ecommerce' );
|
|
},
|
|
'resolve' => static function () {
|
|
/** @var \WPGraphQL\WooCommerce\Utils\QL_Session_Handler $session */
|
|
$session = \WC()->session;
|
|
|
|
return apply_filters( 'graphql_customer_session_token', $session->build_token() );
|
|
},
|
|
]
|
|
);
|
|
}
|
|
if ( in_array( $token_type, [ 'store-api', 'both' ], true ) ) {
|
|
register_graphql_field(
|
|
'LoginPayload',
|
|
'cartToken',
|
|
[
|
|
'type' => 'String',
|
|
'description' => static function () {
|
|
return __( 'A JWT token that can be used in future requests to for WooCommerce session identification', 'graphql-for-ecommerce' );
|
|
},
|
|
'resolve' => static function () {
|
|
/** @var \WPGraphQL\WooCommerce\Utils\QL_Session_Handler $session */
|
|
$session = \WC()->session;
|
|
|
|
return apply_filters( 'graphql_customer_session_token', $session->build_cart_token() );
|
|
},
|
|
]
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Register WooCommerce Stripe Gateway compatibility filters.
|
|
*
|
|
* @return void
|
|
*/
|
|
private static function add_stripe_gateway_filters() {
|
|
add_filter( 'graphql_stripe_process_payment_args', [ self::class, 'woocommerce_gateway_stripe_args' ], 10, 2 );
|
|
}
|
|
|
|
/**
|
|
* Adds extra arguments to the Stripe Gateway process payment call.
|
|
*
|
|
* @param array $gateway_args Arguments to be passed to the gateway `process_payment` method.
|
|
* @param string $payment_method Payment gateway ID.
|
|
*
|
|
* @return array
|
|
*/
|
|
public static function woocommerce_gateway_stripe_args( $gateway_args, $payment_method ) {
|
|
/** @var false|\WC_Order|\WC_Order_Refund $order */
|
|
$order = wc_get_order( $gateway_args[0] );
|
|
if ( false === $order ) {
|
|
return $gateway_args;
|
|
}
|
|
|
|
$stripe_source_id = $order->get_meta( '_stripe_source_id' );
|
|
if ( 'stripe' === $payment_method && ! empty( $stripe_source_id ) ) {
|
|
$gateway_args = [
|
|
$gateway_args[0],
|
|
true,
|
|
false,
|
|
false,
|
|
true,
|
|
];
|
|
}
|
|
|
|
return $gateway_args;
|
|
}
|
|
|
|
/**
|
|
* Register SearchWP/QL Search compatibility filters.
|
|
*
|
|
* @return void
|
|
*/
|
|
private static function add_swp_filters() {
|
|
add_filter( 'graphql_swp_result_possible_types', [ self::class, 'searchwp_result_possible_types' ] );
|
|
}
|
|
|
|
/**
|
|
* Adds product types to QL Search SWPResult possible types.
|
|
*
|
|
* @param array $type_names SWPResults possible types.
|
|
*
|
|
* @return array
|
|
*/
|
|
public static function searchwp_result_possible_types( array $type_names ) {
|
|
if ( in_array( 'Product', $type_names, true ) ) {
|
|
$type_names = array_merge(
|
|
array_filter(
|
|
$type_names,
|
|
static function ( $type_name ) {
|
|
return 'Product' !== $type_name;
|
|
}
|
|
),
|
|
[
|
|
'SimpleProduct',
|
|
'VariableProduct',
|
|
'GroupProduct',
|
|
'ExternalProduct',
|
|
]
|
|
);
|
|
}
|
|
|
|
return $type_names;
|
|
}
|
|
}
|