Skip to content

Commit e7bd5bf

Browse files
authored
Merge pull request #132 from kidunot89/bugfix/checkout-mutation
Checkout mutation bugfix/enhancements
2 parents 1772464 + dcbd241 commit e7bd5bf

14 files changed

+830
-91
lines changed

.travis.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -91,11 +91,11 @@ script:
9191
# Execute unit tests with coverage if specified, otherwise without coverage
9292
- |
9393
if [ ! -z "$WP_VERSION" ]; then
94-
docker-compose run \
94+
docker-compose run --rm \
9595
-e SUITES='acceptance;functional;wpunit' \
9696
-e COVERAGE=${COVERAGE:-0} \
9797
-e DEBUG=${DEBUG:-0} \
98-
testing --rm testing --scale app=0
98+
testing --scale app=0
9999
fi
100100
- |
101101
if [ "$PHPCS" == "1" ]; then

includes/data/mutation/class-checkout-mutation.php

+79-26
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,13 @@ class Checkout_Mutation {
2222
*/
2323
private static $fields;
2424

25+
/**
26+
* Caches customer object. @see get_value.
27+
*
28+
* @var WC_Customer
29+
*/
30+
private $logged_in_customer = null;
31+
2532
/**
2633
* Is registration required to checkout?
2734
*
@@ -63,12 +70,11 @@ protected static function maybe_skip_fieldset( $fieldset_key, $data ) {
6370
*/
6471
public static function prepare_checkout_args( $input, $context, $info ) {
6572
$data = array(
66-
'terms' => (int) isset( $input['terms'] ),
67-
'createaccount' => (int) ! empty( $input['account'] ),
68-
'payment_method' => isset( $input['paymentMethod'] ) ? $input['paymentMethod'] : '',
69-
'shipping_method' => isset( $input['shippingMethod'] ) ? $input['shippingMethod'] : '',
70-
'ship_to_different_address' => ! empty( $input['shipToDifferentAddress'] ) && ! wc_ship_to_billing_address_only(),
71-
'woocommerce_checkout_update_totals' => isset( $input['updateTotals'] ),
73+
'terms' => (int) isset( $input['terms'] ),
74+
'createaccount' => (int) ! empty( $input['account'] ),
75+
'payment_method' => isset( $input['paymentMethod'] ) ? $input['paymentMethod'] : '',
76+
'shipping_method' => isset( $input['shippingMethod'] ) ? $input['shippingMethod'] : '',
77+
'ship_to_different_address' => ! empty( $input['shipToDifferentAddress'] ) && ! wc_ship_to_billing_address_only(),
7278
);
7379
$skipped = array();
7480

@@ -79,11 +85,14 @@ public static function prepare_checkout_args( $input, $context, $info ) {
7985
}
8086

8187
foreach ( $fieldset as $field => $input_key ) {
88+
$key = "{$fieldset_key}_{$field}";
8289
$value = ! empty( $input[ $fieldset_key ][ $input_key ] )
8390
? $input[ $fieldset_key ][ $input_key ]
8491
: null;
8592
if ( $value ) {
86-
$data[ "{$fieldset_key}_{$field}" ] = $value;
93+
$data[ $key ] = $value;
94+
} elseif ( 'billing_country' === $key || 'shipping_country' === $key ) {
95+
$data[ $key ] = self::get_value( $key );
8796
}
8897
}
8998
}
@@ -460,6 +469,11 @@ protected function process_order_without_payment( $order_id ) {
460469
$order = wc_get_order( $order_id );
461470
$order->payment_complete();
462471
wc_empty_cart();
472+
473+
return array(
474+
'result' => 'success',
475+
'redirect' => apply_filters( 'woocommerce_checkout_no_payment_needed_redirect', $order->get_checkout_order_received_url(), $order ),
476+
);
463477
}
464478

465479
/**
@@ -468,12 +482,11 @@ protected function process_order_without_payment( $order_id ) {
468482
* @param array $data Order data.
469483
* @param AppContext $context AppContext instance.
470484
* @param ResolveInfo $info ResolveInfo instance.
485+
* @param array $results Order status.
471486
*
472487
* @throws UserError When validation fails.
473488
*/
474-
public static function process_checkout( $data, $context, $info ) {
475-
WC()->session->set( 'refresh_totals', true );
476-
489+
public static function process_checkout( $data, $context, $info, &$results = null ) {
477490
wc_maybe_define_constant( 'WOOCOMMERCE_CHECKOUT', true );
478491
wc_set_time_limit( 0 );
479492

@@ -491,28 +504,68 @@ public static function process_checkout( $data, $context, $info ) {
491504
// Validate posted data and cart items before proceeding.
492505
self::validate_checkout( $data );
493506

494-
if ( empty( $data['woocommerce_checkout_update_totals'] ) ) {
495-
self::process_customer( $data );
496-
$order_id = WC()->checkout->create_order( $data );
497-
$order = wc_get_order( $order_id );
507+
self::process_customer( $data );
508+
$order_id = WC()->checkout->create_order( $data );
509+
$order = wc_get_order( $order_id );
498510

499-
if ( is_wp_error( $order_id ) ) {
500-
throw new UserError( $order_id->get_error_message() );
501-
}
511+
if ( is_wp_error( $order_id ) ) {
512+
throw new UserError( $order_id->get_error_message() );
513+
}
502514

503-
if ( ! $order ) {
504-
throw new UserError( __( 'Unable to create order.', 'wp-graphql-woocommerce' ) );
505-
}
515+
if ( ! $order ) {
516+
throw new UserError( __( 'Unable to create order.', 'wp-graphql-woocommerce' ) );
517+
}
506518

507-
do_action( 'woocommerce_checkout_order_processed', $order_id, $data, $order );
519+
do_action( 'woocommerce_checkout_order_processed', $order_id, $data, $order );
508520

509-
if ( WC()->cart->needs_payment() ) {
510-
self::process_order_payment( $order_id, $data['payment_method'] );
511-
} else {
512-
self::process_order_without_payment( $order_id );
513-
}
521+
if ( WC()->cart->needs_payment() ) {
522+
$results = self::process_order_payment( $order_id, $data['payment_method'] );
523+
} else {
524+
$results = self::process_order_without_payment( $order_id );
514525
}
515526

516527
return $order_id;
517528
}
529+
530+
/**
531+
* Gets the value either from 3rd party logic or the customer object. Sets the default values in checkout fields.
532+
*
533+
* @param string $input Name of the input we want to grab data for. e.g. billing_country.
534+
* @return string The default value.
535+
*/
536+
public static function get_value( $input ) {
537+
// Allow 3rd parties to short circuit the logic and return their own default value.
538+
$value = apply_filters( 'woocommerce_checkout_get_value', null, $input );
539+
if ( ! is_null( $value ) ) {
540+
return $value;
541+
}
542+
543+
/**
544+
* For logged in customers, pull data from their account rather than the session which may contain incomplete data.
545+
* Another reason is that WC sets shipping address to the billing address on the checkout updates unless the
546+
* "shipToDifferentAddress" is set.
547+
*/
548+
$customer_object = false;
549+
if ( is_user_logged_in() ) {
550+
// Load customer object, but keep it cached to avoid reloading it multiple times.
551+
if ( is_null( self::$logged_in_customer ) ) {
552+
self::$logged_in_customer = new WC_Customer( get_current_user_id(), true );
553+
}
554+
$customer_object = new WC_Customer( get_current_user_id(), true );
555+
}
556+
557+
if ( ! $customer_object ) {
558+
$customer_object = WC()->customer;
559+
}
560+
561+
if ( is_callable( array( $customer_object, "get_$input" ) ) ) {
562+
$value = $customer_object->{"get_$input"}();
563+
} elseif ( $customer_object->meta_exists( $input ) ) {
564+
$value = $customer_object->get_meta( $input, true );
565+
}
566+
if ( '' === $value ) {
567+
$value = null;
568+
}
569+
return apply_filters( 'default_checkout_' . $input, $value, $input );
570+
}
518571
}

includes/mutation/class-checkout.php

+14-6
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,6 @@ public static function get_input_fields() {
5555
'type' => 'Boolean',
5656
'description' => __( 'Ship to a separate address', 'wp-graphql-woocommerce' ),
5757
),
58-
'updateTotals' => array(
59-
'type' => 'Boolean',
60-
'description' => __( 'Update order totals', 'wp-graphql-woocommerce' ),
61-
),
6258
'paymentMethodTitle' => array(
6359
'type' => 'String',
6460
'description' => __( 'Payment method title.', 'woocommerce' ),
@@ -97,6 +93,18 @@ public static function get_output_fields() {
9793
return is_user_logged_in() ? new Customer( get_current_user_id() ) : null;
9894
},
9995
),
96+
'result' => array(
97+
'type' => 'String',
98+
'resolve' => function( $payload ) {
99+
return $payload['result'];
100+
},
101+
),
102+
'redirect' => array(
103+
'type' => 'String',
104+
'resolve' => function( $payload ) {
105+
return $payload['redirect'];
106+
},
107+
),
100108
);
101109
}
102110

@@ -122,7 +130,7 @@ public static function mutate_and_get_payload() {
122130
*/
123131
do_action( 'woocommerce_graphql_before_checkout', $args, $input, $context, $info );
124132

125-
$order_id = Checkout_Mutation::process_checkout( $args, $context, $info );
133+
$order_id = Checkout_Mutation::process_checkout( $args, $context, $info, $results );
126134

127135
if ( is_wp_error( $order_id ) ) {
128136
throw new UserError( $order_id->get_error_message( 'checkout-error' ) );
@@ -138,7 +146,7 @@ public static function mutate_and_get_payload() {
138146
*/
139147
do_action( 'woocommerce_graphql_after_checkout', $order_id, $input, $context, $info );
140148

141-
return array( 'id' => $order_id );
149+
return array_merge( array( 'id' => $order_id ), $results );
142150
} catch ( \Exception $e ) {
143151
Order_Mutation::purge( $order );
144152
throw new UserError( $e->getMessage() );

includes/utils/class-ql-session-handler.php

+3-1
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@
88

99
namespace WPGraphQL\Extensions\WooCommerce\Utils;
1010

11+
use WC_Session_Handler;
12+
1113
/**
1214
* Class - QL_Session_Handler
1315
*/
14-
class QL_Session_Handler extends \WC_Session_Handler {
16+
class QL_Session_Handler extends WC_Session_Handler {
1517
/**
1618
* Encrypt and decrypt
1719
*

0 commit comments

Comments
 (0)