@@ -26,6 +26,7 @@ public static function get_cart_field( $fallback = false ) {
2626 'type ' => 'Cart ' ,
2727 'resolve ' => function ( $ payload ) use ( $ fallback ) {
2828 $ cart = ! empty ( $ payload ['cart ' ] ) ? $ payload ['cart ' ] : null ;
29+
2930 if ( is_null ( $ cart ) && $ fallback ) {
3031 $ cart = Factory::resolve_cart ();
3132 }
@@ -41,9 +42,19 @@ public static function get_cart_field( $fallback = false ) {
4142 * @param AppContext $context AppContext instance.
4243 * @param ResolveInfo $info Query info.
4344 *
45+ * @throws UserError Missing/Invalid input.
46+ *
4447 * @return array
4548 */
4649 public static function prepare_cart_item ( $ input , $ context , $ info ) {
50+ if ( empty ( $ input ['productId ' ] ) ) {
51+ throw new UserError ( __ ( 'No product ID provided ' , 'wp-graphql-woocommerce ' ) );
52+ }
53+
54+ if ( ! \wc_get_product ( $ input ['productId ' ] ) ) {
55+ throw new UserError ( __ ( 'No product found matching the ID provided ' , 'wp-graphql-woocommerce ' ) );
56+ }
57+
4758 $ cart_item_args = array ( $ input ['productId ' ] );
4859 $ cart_item_args [] = ! empty ( $ input ['quantity ' ] ) ? $ input ['quantity ' ] : 1 ;
4960 $ cart_item_args [] = ! empty ( $ input ['variationId ' ] ) ? $ input ['variationId ' ] : 0 ;
@@ -145,6 +156,120 @@ public static function prepare_cart_fee( $input, $context, $info ) {
145156 return apply_filters ( 'graphql_woocommerce_new_cart_fee_data ' , $ cart_item_args , $ input , $ context , $ info );
146157 }
147158
159+
160+ /**
161+ * Validates coupon and checks if application is possible
162+ *
163+ * @param string $code Coupon code.
164+ * @param string $reason Reason for failure.
165+ *
166+ * @return bool
167+ */
168+ public static function validate_coupon ( $ code , &$ reason = '' ) {
169+ // Get the coupon.
170+ $ the_coupon = new \WC_Coupon ( $ code );
171+
172+ // Prevent adding coupons by post ID.
173+ if ( $ the_coupon ->get_code () !== $ code ) {
174+ $ reason = __ ( 'No coupon found with the code provided ' , 'wp-graphql-woocommerce ' );
175+ return false ;
176+ }
177+
178+ // Check it can be used with cart.
179+ if ( ! $ the_coupon ->is_valid () ) {
180+ $ reason = $ the_coupon ->get_error_message ();
181+ return false ;
182+ }
183+
184+ // Check if applied.
185+ if ( \WC ()->cart ->has_discount ( $ code ) ) {
186+ $ reason = __ ( 'This coupon has already been applied to the cart ' , 'wp-graphql-woocommerce ' );
187+ return false ;
188+ }
189+
190+ return true ;
191+ }
192+
193+ /**
194+ * Validates shipping method by checking comparing against shipping package.
195+ *
196+ * @param string $shipping_method Shipping method being validated.
197+ * @param integer $index Index of the shipping package.
198+ * @param string $reason Reason for failure.
199+ *
200+ * @return bool
201+ */
202+ public static function validate_shipping_method ( $ shipping_method , $ index , &$ reason = '' ) {
203+ // Get available shipping packages.
204+ $ available_packages = \WC ()->cart ->needs_shipping ()
205+ ? \WC ()->shipping ()->calculate_shipping ( \WC ()->cart ->get_shipping_packages () )
206+ : array ();
207+
208+ if ( ! isset ( $ available_packages [ $ index ] ) ) {
209+ $ reason = sprintf (
210+ /* translators: %d: Package index */
211+ __ ( 'No shipping packages available for corresponding index %d ' , 'wp-graphql-woocommerce ' ),
212+ $ index
213+ );
214+
215+ return false ;
216+ }
217+
218+ $ package = $ available_packages [ $ index ];
219+ $ chosen_rate_index = array_search ( $ shipping_method , wp_list_pluck ( $ package ['rates ' ], 'id ' ), true );
220+
221+ if ( false !== $ chosen_rate_index ) {
222+ return true ;
223+ }
224+
225+ $ product_names = array ();
226+ foreach ( $ package ['contents ' ] as $ item_id => $ values ) {
227+ $ product_names [ $ item_id ] = \html_entity_decode ( $ values ['data ' ]->get_name () . ' × ' . $ values ['quantity ' ] );
228+ }
229+
230+ // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
231+ $ product_names = apply_filters ( 'woocommerce_shipping_package_details_array ' , $ product_names , $ package );
232+
233+ $ reason = sprintf (
234+ /* translators: %1$s: shipping method ID, %2$s: package contents */
235+ __ ( '"%1$s" is not an available shipping method for shipping package "%2$s" ' , 'wp-graphql-woocommerce ' ),
236+ $ shipping_method ,
237+ implode ( ', ' , $ product_names )
238+ );
239+
240+ return false ;
241+ }
242+
243+ /**
244+ * Validates and prepares posted shipping methods for the user session.
245+ *
246+ * @param array $posted_shipping_methods Chosen shipping methods.
247+ *
248+ * @throws UserError Invalid shipping method.
249+ *
250+ * @return array
251+ */
252+ public static function prepare_shipping_methods ( $ posted_shipping_methods ) {
253+ // Get current shipping methods.
254+ $ chosen_shipping_methods = \WC ()->session ->get ( 'chosen_shipping_methods ' );
255+
256+ // Update current shipping methods.
257+ foreach ( $ posted_shipping_methods as $ package => $ chosen_method ) {
258+ if ( empty ( $ chosen_method ) ) {
259+ continue ;
260+ }
261+
262+ $ reason = '' ;
263+ if ( self ::validate_shipping_method ( $ chosen_method , $ package , $ reason ) ) {
264+ $ chosen_shipping_methods [ $ package ] = $ chosen_method ;
265+ } else {
266+ throw new UserError ( $ reason );
267+ }
268+ }
269+
270+ return $ chosen_shipping_methods ;
271+ }
272+
148273 /**
149274 * Validate CartItemQuantityInput item.
150275 *
0 commit comments