Skip to content

Commit e5ed555

Browse files
authored
Checkout mutation updates (#229)
* "isPaid" and "transactionId" fields added to "CheckoutInput".
1 parent c9a4a32 commit e5ed555

File tree

6 files changed

+254
-22
lines changed

6 files changed

+254
-22
lines changed

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

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -463,11 +463,14 @@ protected function process_order_payment( $order_id, $payment_method ) {
463463
* Process an order that doesn't require payment.
464464
*
465465
* @since 3.0.0
466-
* @param int $order_id Order ID.
466+
* @param int $order_id Order ID.
467+
* @param string $transaction_id Payment transaction ID.
468+
*
469+
* @return array
467470
*/
468-
protected function process_order_without_payment( $order_id ) {
471+
protected function process_order_without_payment( $order_id, $transaction_id = '' ) {
469472
$order = wc_get_order( $order_id );
470-
$order->payment_complete();
473+
$order->payment_complete( $transaction_id );
471474
wc_empty_cart();
472475

473476
return array(
@@ -480,13 +483,14 @@ protected function process_order_without_payment( $order_id ) {
480483
* Process the checkout.
481484
*
482485
* @param array $data Order data.
486+
* @param array $input Input data describing order.
483487
* @param AppContext $context AppContext instance.
484488
* @param ResolveInfo $info ResolveInfo instance.
485489
* @param array $results Order status.
486490
*
487491
* @throws UserError When validation fails.
488492
*/
489-
public static function process_checkout( $data, $context, $info, &$results = null ) {
493+
public static function process_checkout( $data, $input, $context, $info, &$results = null ) {
490494
wc_maybe_define_constant( 'WOOCOMMERCE_CHECKOUT', true );
491495
wc_set_time_limit( 0 );
492496

@@ -518,10 +522,47 @@ public static function process_checkout( $data, $context, $info, &$results = nul
518522

519523
do_action( 'woocommerce_checkout_order_processed', $order_id, $data, $order );
520524

521-
if ( WC()->cart->needs_payment() ) {
525+
if ( WC()->cart->needs_payment() && ( empty( $input['isPaid'] ) || false === $input['isPaid'] ) ) {
522526
$results = self::process_order_payment( $order_id, $data['payment_method'] );
523527
} else {
524-
$results = self::process_order_without_payment( $order_id );
528+
$transaction_id = ! empty( $input['transactionId'] ) ? $input['transactionId'] : '';
529+
530+
/**
531+
* Use this to do some last minute transaction ID validation.
532+
*
533+
* @param bool $is_valid Is transaction ID valid.
534+
* @param WC_Order $order Order being processed.
535+
* @param String|null $transaction_id Order payment transaction ID.
536+
* @param array $data Order data.
537+
* @param array $input Order raw input data.
538+
* @param AppContext $context Request's AppContext instance.
539+
* @param ResolveInfo $info Request's ResolveInfo instance.
540+
*/
541+
$valid = apply_filters(
542+
'graphql_checkout_prepaid_order_validation',
543+
true,
544+
$order,
545+
$transaction_id,
546+
$data,
547+
$input,
548+
$context,
549+
$info
550+
);
551+
552+
if ( $valid ) {
553+
$results = self::process_order_without_payment( $order_id, $transaction_id );
554+
} else {
555+
$results = array(
556+
'result' => 'failed',
557+
'redirect' => apply_filters(
558+
'graphql_woocommerce_checkout_payment_failed_redirect',
559+
$order->get_checkout_payment_url(),
560+
$order,
561+
$order_id,
562+
$transaction_id
563+
),
564+
);
565+
}
525566
}
526567

527568
return $order_id;

includes/model/class-order.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ public function __construct( $id ) {
3838
'id',
3939
'orderId',
4040
'orderNumber',
41+
'status',
4142
'date',
4243
'modified',
4344
'datePaid',

includes/mutation/class-checkout.php

Lines changed: 9 additions & 5 deletions
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-
'paymentMethodTitle' => array(
59-
'type' => 'String',
60-
'description' => __( 'Payment method title.', 'woocommerce' ),
61-
),
6258
'billing' => array(
6359
'type' => 'CustomerAddressInput',
6460
'description' => __( 'Order billing address', 'wp-graphql-woocommerce' ),
@@ -71,6 +67,14 @@ public static function get_input_fields() {
7167
'type' => 'CreateAccountInput',
7268
'description' => __( 'Create new customer account', 'wp-graphql-woocommerce' ),
7369
),
70+
'transactionId' => array(
71+
'type' => 'String',
72+
'description' => __( 'Order transaction ID', 'wp-graphql-woocommerce' ),
73+
),
74+
'isPaid' => array(
75+
'type' => 'Boolean',
76+
'description' => __( 'Define if the order is paid. It will set the status to processing and reduce stock items.', 'wp-graphql-woocommerce' ),
77+
),
7478
'metaData' => array(
7579
'type' => array( 'list_of' => 'MetaDataInput' ),
7680
'description' => __( 'Order meta data', 'wp-graphql-woocommerce' ),
@@ -134,7 +138,7 @@ public static function mutate_and_get_payload() {
134138
*/
135139
do_action( 'woocommerce_graphql_before_checkout', $args, $input, $context, $info );
136140

137-
$order_id = Checkout_Mutation::process_checkout( $args, $context, $info, $results );
141+
$order_id = Checkout_Mutation::process_checkout( $args, $input, $context, $info, $results );
138142

139143
if ( is_wp_error( $order_id ) ) {
140144
throw new UserError( $order_id->get_error_message( 'checkout-error' ) );

tests/_support/Helper/crud-helpers/order.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -329,8 +329,8 @@ public function print_restricted_query( $id ) {
329329
'orderVersion' => null,
330330
'date' => $data->get_date_created()->__toString(),
331331
'modified' => $data->get_date_modified()->__toString(),
332-
'status' => null,
333-
'discountTotal' => \wc_graphql_price( $data->get_discount_total(), array( 'currency' => $data->get_currency() ) ),
332+
'status' => WPEnumType::get_safe_name( $data->get_status() ),
333+
'discountTotal' => \wc_graphql_price( $data->get_discount_total(), array( 'currency' => $data->get_currency() ) ),
334334
'discountTax' => \wc_graphql_price( $data->get_discount_tax(), array( 'currency' => $data->get_currency() ) ),
335335
'shippingTotal' => \wc_graphql_price( $data->get_shipping_total(), array( 'currency' => $data->get_currency() ) ),
336336
'shippingTax' => \wc_graphql_price( $data->get_shipping_tax(), array( 'currency' => $data->get_currency() ) ),

tests/_support/Helper/crud-helpers/product.php

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -140,17 +140,21 @@ public function create_grouped( $args = array() ) {
140140

141141
public function create_variable( $args = array() ) {
142142
$product = new WC_Product_Variable();
143-
$product->set_props(
144-
array_merge(
145-
array(
146-
'name' => $this->dummy->product(),
147-
'slug' => $this->next_slug(),
148-
'sku' => 'DUMMY VARIABLE SKU ' . $this->index,
149-
),
150-
$args
151-
)
143+
$props = array_merge(
144+
array(
145+
'name' => $this->dummy->product(),
146+
'slug' => $this->next_slug(),
147+
'sku' => 'DUMMY VARIABLE SKU ' . $this->index,
148+
),
149+
$args
152150
);
153151

152+
foreach ( $props as $key => $value ) {
153+
if ( is_callable( array( $product, "set_{$key}" ) ) ) {
154+
$product->{"set_{$key}"}( $value );
155+
}
156+
}
157+
154158
if ( ! empty( $args['meta_data'] ) ) {
155159
$product->set_meta_data( $args['meta_data'] );
156160
}

tests/wpunit/CheckoutMutationsTest.php

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -851,4 +851,186 @@ function( $item ) {
851851

852852
$this->assertEquals( $expected, $actual );
853853
}
854+
855+
public function testCheckoutOrderMutationWithPrepaidOrder() {
856+
update_option( 'woocommerce_enable_guest_checkout', 'yes' );
857+
$product_ids = array(
858+
$this->product->create_simple(
859+
array(
860+
'virtual' => true,
861+
'downloadable' => true,
862+
)
863+
),
864+
$this->product->create_simple(
865+
array(
866+
'virtual' => true,
867+
'downloadable' => true,
868+
)
869+
),
870+
);
871+
$coupon = new WC_Coupon(
872+
$this->coupon->create( array( 'product_ids' => $product_ids ) )
873+
);
874+
WC()->cart->add_to_cart( $product_ids[0], 3 );
875+
WC()->cart->add_to_cart( $product_ids[1], 6 );
876+
WC()->cart->apply_coupon( $coupon->get_code() );
877+
878+
$input = array(
879+
'clientMutationId' => 'someId',
880+
'paymentMethod' => 'bacs',
881+
'isPaid' => true,
882+
'transactionId' => 'transaction_id',
883+
'shippingMethod' => 'flat rate',
884+
'billing' => array(
885+
'firstName' => 'May',
886+
'lastName' => 'Parker',
887+
'address1' => '20 Ingram St',
888+
'city' => 'New York City',
889+
'state' => 'NY',
890+
'postcode' => '12345',
891+
'country' => 'US',
892+
'email' => '[email protected]',
893+
'phone' => '555-555-1234',
894+
),
895+
'shipping' => array(
896+
'firstName' => 'May',
897+
'lastName' => 'Parker',
898+
'address1' => '20 Ingram St',
899+
'city' => 'New York City',
900+
'state' => 'NY',
901+
'postcode' => '12345',
902+
'country' => 'US',
903+
),
904+
);
905+
906+
/**
907+
* Assertion One
908+
*
909+
* Test mutation and input.
910+
*/
911+
$actual = $this->checkout( $input );
912+
913+
// use --debug flag to view.
914+
codecept_debug( $actual );
915+
916+
$this->assertArrayHasKey('data', $actual );
917+
$this->assertArrayHasKey('checkout', $actual['data'] );
918+
$this->assertArrayHasKey('order', $actual['data']['checkout'] );
919+
$this->assertArrayHasKey('id', $actual['data']['checkout']['order'] );
920+
$this->assertEquals('COMPLETED', $actual['data']['checkout']['order']['status'] );
921+
$order = \WC_Order_Factory::get_order( $actual['data']['checkout']['order']['orderId'] );
922+
923+
// Get Available payment gateways.
924+
$available_gateways = WC()->payment_gateways->get_available_payment_gateways();
925+
926+
$expected = array(
927+
'data' => array(
928+
'checkout' => array(
929+
'clientMutationId' => 'someId',
930+
'order' => array_merge(
931+
$this->order->print_restricted_query( $order->get_id() ),
932+
array(
933+
'metaData' => array(
934+
array(
935+
'key' => 'is_vat_exempt',
936+
'value' => 'no',
937+
),
938+
),
939+
'couponLines' => array(
940+
'nodes' => array_reverse(
941+
array_map(
942+
function( $item ) {
943+
return array(
944+
'itemId' => $item->get_id(),
945+
'orderId' => $item->get_order_id(),
946+
'code' => $item->get_code(),
947+
'discount' => ! empty( $item->get_discount() ) ? $item->get_discount() : null,
948+
'discountTax' => ! empty( $item->get_discount_tax() ) ? $item->get_discount_tax() : null,
949+
'coupon' => null,
950+
);
951+
},
952+
$order->get_items( 'coupon' )
953+
)
954+
),
955+
),
956+
'feeLines' => array(
957+
'nodes' => array_reverse(
958+
array_map(
959+
function( $item ) {
960+
return array(
961+
'itemId' => $item->get_id(),
962+
'orderId' => $item->get_order_id(),
963+
'amount' => $item->get_amount(),
964+
'name' => $item->get_name(),
965+
'taxStatus' => strtoupper( $item->get_tax_status() ),
966+
'total' => $item->get_total(),
967+
'totalTax' => ! empty( $item->get_total_tax() ) ? $item->get_total_tax() : null,
968+
'taxClass' => ! empty( $item->get_tax_class() )
969+
? WPEnumType::get_safe_name( $item->get_tax_class() )
970+
: 'STANDARD',
971+
);
972+
},
973+
$order->get_items( 'fee' )
974+
)
975+
),
976+
),
977+
'shippingLines' => null,
978+
'taxLines' => array(
979+
'nodes' => array_reverse(
980+
array_map(
981+
function( $item ) {
982+
return array(
983+
'rateCode' => $item->get_rate_code(),
984+
'label' => $item->get_label(),
985+
'taxTotal' => $item->get_tax_total(),
986+
'shippingTaxTotal' => $item->get_shipping_tax_total(),
987+
'isCompound' => $item->is_compound(),
988+
'taxRate' => array( 'rateId' => $item->get_rate_id() ),
989+
);
990+
},
991+
$order->get_items( 'tax' )
992+
)
993+
),
994+
),
995+
'lineItems' => array(
996+
'nodes' => array_values(
997+
array_map(
998+
function( $item ) {
999+
return array(
1000+
'productId' => $item->get_product_id(),
1001+
'variationId' => ! empty( $item->get_variation_id() )
1002+
? $item->get_variation_id()
1003+
: null,
1004+
'quantity' => $item->get_quantity(),
1005+
'taxClass' => ! empty( $item->get_tax_class() )
1006+
? strtoupper( $item->get_tax_class() )
1007+
: 'STANDARD',
1008+
'subtotal' => ! empty( $item->get_subtotal() ) ? $item->get_subtotal() : null,
1009+
'subtotalTax' => ! empty( $item->get_subtotal_tax() ) ? $item->get_subtotal_tax() : null,
1010+
'total' => ! empty( $item->get_total() ) ? $item->get_total() : null,
1011+
'totalTax' => ! empty( $item->get_total_tax() ) ? $item->get_total_tax() : null,
1012+
'taxStatus' => strtoupper( $item->get_tax_status() ),
1013+
'product' => array( 'id' => $this->product->to_relay_id( $item->get_product_id() ) ),
1014+
'variation' => ! empty( $item->get_variation_id() )
1015+
? array(
1016+
'id' => $this->variation->to_relay_id( $item->get_variation_id() )
1017+
)
1018+
: null,
1019+
);
1020+
},
1021+
$order->get_items()
1022+
)
1023+
),
1024+
),
1025+
)
1026+
),
1027+
'customer' => null,
1028+
'result' => 'success',
1029+
'redirect' => $available_gateways['bacs']->process_payment( $order->get_id() )['redirect'],
1030+
),
1031+
)
1032+
);
1033+
1034+
$this->assertEquals( $expected, $actual );
1035+
}
8541036
}

0 commit comments

Comments
 (0)