• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

wp-graphql / wp-graphql-woocommerce / 23663706692

27 Mar 2026 07:25PM UTC coverage: 89.424% (+0.3%) from 89.165%
23663706692

Pull #1002

github

web-flow
Merge 49b913dda into 66f840efc
Pull Request #1002: feat: WC Settings API, compatibility refactor, HPOS fix

575 of 643 new or added lines in 21 files covered. (89.42%)

15812 of 17682 relevant lines covered (89.42%)

284.73 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

60.26
/includes/class-compatibility.php
1
<?php
2
/**
3
 * Compatibility integrations with third-party plugins.
4
 *
5
 * @package \WPGraphQL\WooCommerce
6
 * @since   TBD
7
 */
8

9
namespace WPGraphQL\WooCommerce;
10

11
use GraphQL\Error\UserError;
12
use WPGraphQL\WooCommerce\Model\Customer;
13

14
/**
15
 * Class Compatibility
16
 */
17
class Compatibility {
18
        /**
19
         * Register all compatibility filters.
20
         *
21
         * @return void
22
         */
23
        public static function setup() {
24
                self::register_wc_admin_settings();
58✔
25
                self::add_acf_filters();
58✔
26
                self::add_jwt_auth_filters();
58✔
27
                self::add_stripe_gateway_filters();
58✔
28
                self::add_swp_filters();
58✔
29
        }
30

31
        /**
32
         * Register WC admin settings for GraphQL requests.
33
         *
34
         * WooCommerce only registers settings groups during rest_api_init.
35
         * We need them available for GraphQL settings queries and mutations.
36
         *
37
         * @return void
38
         */
39
        private static function register_wc_admin_settings() {
40
                if ( method_exists( WC(), 'register_wp_admin_settings' ) ) {
58✔
41
                        WC()->register_wp_admin_settings(); // @phpstan-ignore method.private (public since WC 9.0)
58✔
42
                }
43
        }
44

45
        /**
46
         * Register WPGraphQL ACF compatibility filters.
47
         *
48
         * @return void
49
         */
50
        private static function add_acf_filters() {
51
                add_filter( 'graphql_acf_get_root_id', [ self::class, 'resolve_crud_root_id' ], 10, 2 );
58✔
52
                add_filter( 'graphql_acf_post_object_source', [ self::class, 'resolve_post_object_source' ], 10, 2 );
58✔
53
        }
54

55
        /**
56
         * Resolve post object ID from CRUD object Model.
57
         *
58
         * @param integer|null $id    Post object database ID.
59
         * @param mixed        $root  Root resolver.
60
         *
61
         * @return integer|null
62
         */
63
        public static function resolve_crud_root_id( $id, $root ) {
NEW
64
                if ( $root instanceof Model\WC_Post ) {
×
NEW
65
                        $id = absint( $root->ID );
×
66
                }
67

NEW
68
                return $id;
×
69
        }
70

71
        /**
72
         * Filters ACF "post_object" field type resolver to ensure that
73
         * the proper Type source is provided for WooCommerce CPTs.
74
         *
75
         * @param mixed|null $source  source of the data being provided.
76
         * @param mixed|null $value  Post ID.
77
         *
78
         * @return mixed|null
79
         */
80
        public static function resolve_post_object_source( $source, $value ) {
NEW
81
                $post = get_post( $value );
×
NEW
82
                if ( $post instanceof \WP_Post ) {
×
NEW
83
                        switch ( $post->post_type ) {
×
NEW
84
                                case 'shop_coupon':
×
NEW
85
                                        $source = new Model\Coupon( $post->ID );
×
NEW
86
                                        break;
×
NEW
87
                                case 'shop_order':
×
NEW
88
                                        $source = new Model\Order( $post->ID );
×
NEW
89
                                        break;
×
NEW
90
                                case 'product':
×
NEW
91
                                        $source = new Model\Product( $post->ID );
×
NEW
92
                                        break;
×
NEW
93
                                case 'product_variation':
×
NEW
94
                                        $source = new Model\Product_Variation( $post->ID );
×
NEW
95
                                        break;
×
96
                        }
97
                }
98

NEW
99
                return $source;
×
100
        }
101

102
        /**
103
         * Get the JWT auth class if available.
104
         *
105
         * @return string|null
106
         */
107
        public static function get_auth_class() {
108
                if ( class_exists( 'WPGraphQL\JWT_Authentication\Auth' ) ) {
74✔
109
                        return \WPGraphQL\JWT_Authentication\Auth::class;
74✔
110
                } elseif ( class_exists( 'WPGraphQL\Login\Auth\TokenManager' ) ) {
×
111
                        return \WPGraphQL\Login\Auth\TokenManager::class;
×
112
                }
113

NEW
114
                return null;
×
115
        }
116

117
        /**
118
         * Get the auth token for a user.
119
         *
120
         * @param \WP_User $user The user object.
121
         *
122
         * @throws \GraphQL\Error\UserError If the token cannot be retrieved.
123
         *
124
         * @return string|null
125
         */
126
        public static function get_auth_token( \WP_User $user ) {
127
                $auth_class = self::get_auth_class();
16✔
128

129
                if ( ! $auth_class ) {
16✔
130
                        return null;
×
131
                }
132

133
                /**
134
                 * @var \WP_Error|string|null $token
135
                 */
136
                $token = null;
16✔
137
                if ( 'WPGraphQL\JWT_Authentication\Auth' === $auth_class ) {
16✔
138
                        $token = $auth_class::get_token( $user );
16✔
139
                } elseif ( 'WPGraphQL\Login\Auth\TokenManager' === $auth_class ) {
×
140
                        $token = $auth_class::get_auth_token( $user );
×
141
                }
142

143
                if ( is_wp_error( $token ) ) {
16✔
144
                        throw new UserError( $token->get_error_message() );
×
145
                }
146

147
                return $token;
16✔
148
        }
149

150
        /**
151
         * Get the refresh token for a user.
152
         *
153
         * @param \WP_User $user The user object.
154
         *
155
         * @throws \GraphQL\Error\UserError If the token cannot be retrieved.
156
         *
157
         * @return string|null
158
         */
159
        public static function get_refresh_token( \WP_User $user ) {
160
                $auth_class = self::get_auth_class();
16✔
161

162
                if ( ! $auth_class ) {
16✔
163
                        return null;
×
164
                }
165

166
                /**
167
                 * @var \WP_Error|string|null $refresh_token
168
                 */
169
                $refresh_token = $auth_class::get_refresh_token( $user );
16✔
170

171
                if ( is_wp_error( $refresh_token ) ) {
16✔
172
                        throw new UserError( $refresh_token->get_error_message() );
×
173
                }
174

175
                return $refresh_token;
16✔
176
        }
177

178
        /**
179
         * Register WPGraphQL JWT Authentication compatibility filters.
180
         *
181
         * @return void
182
         */
183
        private static function add_jwt_auth_filters() {
184
                $auth_class = self::get_auth_class();
58✔
185
                if ( is_null( $auth_class ) ) {
58✔
NEW
186
                        return;
×
187
                }
188

189
                add_filter( 'graphql_jwt_user_types', [ self::class, 'add_customer_to_jwt_user_types' ], 10 );
58✔
190
                add_filter( 'graphql_registerCustomerPayload_fields', [ self::class, 'add_jwt_output_fields' ], 10, 3 );
58✔
191
                add_filter( 'graphql_updateCustomerPayload_fields', [ self::class, 'add_jwt_output_fields' ], 10, 3 );
58✔
192
                add_action( 'graphql_register_types', [ self::class, 'add_customer_to_login_payload' ], 10 );
58✔
193
        }
194

195
        /**
196
         * Adds Customer type to the JWT User type list.
197
         *
198
         * @param array $types JWT User types.
199
         * @return array
200
         */
201
        public static function add_customer_to_jwt_user_types( array $types ) {
202
                $types[] = 'Customer';
560✔
203

204
                return $types;
560✔
205
        }
206

207
        /**
208
         * Adds all JWT related fields to the Customer mutation output.
209
         *
210
         * @param array                             $fields         Mutation output field definitions.
211
         * @param \WPGraphQL\Type\WPInputObjectType $object_type    The WPInputObjectType the fields are be added to.
212
         * @param \WPGraphQL\Registry\TypeRegistry  $type_registry  TypeRegistry instance.
213
         *
214
         * @return array
215
         */
216
        public static function add_jwt_output_fields( $fields, $object_type, $type_registry ): array {
217
                return array_merge(
324✔
218
                        $fields,
324✔
219
                        [
324✔
220
                                'authToken'    => [
324✔
221
                                        'type'        => $type_registry->get_type( 'String' ),
324✔
222
                                        'description' => __( 'JWT Token that can be used in future requests for Authentication', 'wp-graphql-woocommerce' ),
324✔
223
                                        'resolve'     => static function ( $payload ) {
324✔
224
                                                $user = get_user_by( 'ID', $payload['id'] );
16✔
225

226
                                                if ( ! $user ) {
16✔
227
                                                        throw new UserError( __( 'User not found.', 'wp-graphql-woocommerce' ) );
×
228
                                                }
229

230
                                                return self::get_auth_token( $user );
16✔
231
                                        },
324✔
232
                                ],
324✔
233
                                'refreshToken' => [
324✔
234
                                        'type'        => $type_registry->get_type( 'String' ),
324✔
235
                                        'description' => __( '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.', 'wp-graphql-woocommerce' ),
324✔
236
                                        'resolve'     => static function ( $payload ) {
324✔
237
                                                $user = get_user_by( 'ID', $payload['id'] );
16✔
238

239
                                                if ( ! $user ) {
16✔
240
                                                        throw new UserError( __( 'User not found.', 'wp-graphql-woocommerce' ) );
×
241
                                                }
242

243
                                                return self::get_refresh_token( $user );
16✔
244
                                        },
324✔
245
                                ],
324✔
246
                        ]
324✔
247
                );
324✔
248
        }
249

250
        /**
251
         * Adds "customer" field to "login" mutation payload.
252
         *
253
         * @return void
254
         */
255
        public static function add_customer_to_login_payload() {
256
                register_graphql_field(
560✔
257
                        'LoginPayload',
560✔
258
                        'customer',
560✔
259
                        [
560✔
260
                                'type'        => 'Customer',
560✔
261
                                'description' => __( 'Customer object of authenticated user.', 'wp-graphql-woocommerce' ),
560✔
262
                                'resolve'     => static function ( $payload ) {
560✔
263
                                        $id = $payload['id'];
30✔
264
                                        return new Customer( $id );
30✔
265
                                },
560✔
266
                        ]
560✔
267
                );
560✔
268

269
                if ( ! wc_graphql_is_session_handler_disabled() ) {
560✔
270
                        $token_type = woographql_setting( 'set_session_token_type', 'legacy' );
560✔
271
                        if ( in_array( $token_type, [ 'legacy', 'both' ], true ) ) {
560✔
272
                                register_graphql_field(
558✔
273
                                        'LoginPayload',
558✔
274
                                        'sessionToken',
558✔
275
                                        [
558✔
276
                                                'type'        => 'String',
558✔
277
                                                'description' => __( 'A JWT token that can be used in future requests to for WooCommerce session identification', 'wp-graphql-woocommerce' ),
558✔
278
                                                'resolve'     => static function () {
558✔
279
                                                        /** @var \WPGraphQL\WooCommerce\Utils\QL_Session_Handler $session */
280
                                                        $session = \WC()->session;
30✔
281

282
                                                        return apply_filters( 'graphql_customer_session_token', $session->build_token() );
30✔
283
                                                },
558✔
284
                                        ]
558✔
285
                                );
558✔
286
                        }
287
                        if ( in_array( $token_type, [ 'store-api', 'both' ], true ) ) {
560✔
288
                                register_graphql_field(
4✔
289
                                        'LoginPayload',
4✔
290
                                        'cartToken',
4✔
291
                                        [
4✔
292
                                                'type'        => 'String',
4✔
293
                                                'description' => __( 'A JWT token that can be used in future requests to for WooCommerce session identification', 'wp-graphql-woocommerce' ),
4✔
294
                                                'resolve'     => static function () {
4✔
295
                                                        /** @var \WPGraphQL\WooCommerce\Utils\QL_Session_Handler $session */
296
                                                        $session = \WC()->session;
×
297

298
                                                        return apply_filters( 'graphql_customer_session_token', $session->build_cart_token() );
×
299
                                                },
4✔
300
                                        ]
4✔
301
                                );
4✔
302
                        }
303
                }
304
        }
305

306
        /**
307
         * Register WooCommerce Stripe Gateway compatibility filters.
308
         *
309
         * @return void
310
         */
311
        private static function add_stripe_gateway_filters() {
312
                add_filter( 'graphql_stripe_process_payment_args', [ self::class, 'woocommerce_gateway_stripe_args' ], 10, 2 );
58✔
313
        }
314

315
        /**
316
         * Adds extra arguments to the Stripe Gateway process payment call.
317
         *
318
         * @param array  $gateway_args    Arguments to be passed to the gateway `process_payment` method.
319
         * @param string $payment_method  Payment gateway ID.
320
         *
321
         * @return array
322
         */
323
        public static function woocommerce_gateway_stripe_args( $gateway_args, $payment_method ) {
324
                /** @var false|\WC_Order|\WC_Order_Refund $order */
NEW
325
                $order = wc_get_order( $gateway_args[0] );
×
NEW
326
                if ( false === $order ) {
×
NEW
327
                        return $gateway_args;
×
328
                }
329

NEW
330
                $stripe_source_id = $order->get_meta( '_stripe_source_id' );
×
NEW
331
                if ( 'stripe' === $payment_method && ! empty( $stripe_source_id ) ) {
×
NEW
332
                        $gateway_args = [
×
NEW
333
                                $gateway_args[0],
×
NEW
334
                                true,
×
NEW
335
                                false,
×
NEW
336
                                false,
×
NEW
337
                                true,
×
NEW
338
                        ];
×
339
                }
340

NEW
341
                return $gateway_args;
×
342
        }
343

344
        /**
345
         * Register SearchWP/QL Search compatibility filters.
346
         *
347
         * @return void
348
         */
349
        private static function add_swp_filters() {
350
                add_filter( 'graphql_swp_result_possible_types', [ self::class, 'searchwp_result_possible_types' ] );
58✔
351
        }
352

353
        /**
354
         * Adds product types to QL Search SWPResult possible types.
355
         *
356
         * @param array $type_names SWPResults possible types.
357
         *
358
         * @return array
359
         */
360
        public static function searchwp_result_possible_types( array $type_names ) {
NEW
361
                if ( in_array( 'Product', $type_names, true ) ) {
×
NEW
362
                        $type_names = array_merge(
×
NEW
363
                                array_filter(
×
NEW
364
                                        $type_names,
×
NEW
365
                                        static function ( $type_name ) {
×
NEW
366
                                                return 'Product' !== $type_name;
×
NEW
367
                                        }
×
NEW
368
                                ),
×
NEW
369
                                [
×
NEW
370
                                        'SimpleProduct',
×
NEW
371
                                        'VariableProduct',
×
NEW
372
                                        'GroupProduct',
×
NEW
373
                                        'ExternalProduct',
×
NEW
374
                                ]
×
NEW
375
                        );
×
376
                }
377

NEW
378
                return $type_names;
×
379
        }
380
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc