diff --git a/README.md b/README.md
index e9cbf149..d42fd6ab 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@
Docs • AxisTaylor • Join Slack
-[](https://github.com/wp-graphql/wp-graphql-woocommerce/actions?query=workflow%3A%22WordPress+Coding+Standards%22) [](https://coveralls.io/github/wp-graphql/wp-graphql-woocommerce?branch=develop) [](https://opencollective.com/woographql) [](https://github.com/wp-graphql/wp-graphql-woocommerce/actions?query=workflow%3A%22Testing+Integration%22)
+[](https://github.com/wp-graphql/wp-graphql-woocommerce/actions?query=workflow%3A%22Testing+Integration%22) [](https://github.com/wp-graphql/wp-graphql-woocommerce/actions?query=workflow%3A%22WordPress+Coding+Standards%22) [](https://coveralls.io/github/wp-graphql/wp-graphql-woocommerce?branch=develop) [](https://opencollective.com/woographql)
## Quick Install
1. Install & activate [WooCommerce](https://woocommerce.com/)
diff --git a/bin/install-test-env.sh b/bin/install-test-env.sh
index 976ded0a..a12c0e5b 100644
--- a/bin/install-test-env.sh
+++ b/bin/install-test-env.sh
@@ -149,6 +149,8 @@ configure_wordpress() {
setup_woocommerce() {
echo "Installing & Activating WooCommerce"
wp plugin install woocommerce --activate
+ echo "Installing & Activating WooCommerce-Gateway-Stripe"
+ wp plugin install woocommerce-gateway-stripe --activate
}
setup_wpgraphql() {
diff --git a/includes/data/mutation/class-checkout-mutation.php b/includes/data/mutation/class-checkout-mutation.php
index 00600401..599382ae 100644
--- a/includes/data/mutation/class-checkout-mutation.php
+++ b/includes/data/mutation/class-checkout-mutation.php
@@ -525,6 +525,11 @@ class Checkout_Mutation {
throw new UserError( __( 'Unable to create order.', 'wp-graphql-woocommerce' ) );
}
+ // Add meta data.
+ if ( ! empty( $input['metaData'] ) ) {
+ self::add_order_meta( $order_id, $input['metaData'], $input, $context );
+ }
+
// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
do_action( 'woocommerce_checkout_order_processed', $order_id, $data, $order );
@@ -633,7 +638,7 @@ class Checkout_Mutation {
if ( $meta_data ) {
foreach ( $meta_data as $meta ) {
- $order->update_meta_data( $meta['key'], $meta['value'], isset( $meta['id'] ) ? $meta['id'] : '' );
+ $order->add_meta_data( $meta['key'], $meta['value'] );
}
}
diff --git a/includes/mutation/class-checkout.php b/includes/mutation/class-checkout.php
index 454b7dd4..9b5bec4f 100644
--- a/includes/mutation/class-checkout.php
+++ b/includes/mutation/class-checkout.php
@@ -146,11 +146,6 @@ class Checkout {
throw new UserError( $order_id->get_error_message( 'checkout-error' ) );
}
- // Add meta data.
- if ( ! empty( $input['metaData'] ) ) {
- Checkout_Mutation::add_order_meta( $order_id, $input['metaData'], $input, $context, $info );
- }
-
/**
* Action called after checking out.
*
diff --git a/tests/wpunit/CheckoutMutationsTest.php b/tests/wpunit/CheckoutMutationTest.php
similarity index 70%
rename from tests/wpunit/CheckoutMutationsTest.php
rename to tests/wpunit/CheckoutMutationTest.php
index 2aaa4ca1..5f23df99 100644
--- a/tests/wpunit/CheckoutMutationsTest.php
+++ b/tests/wpunit/CheckoutMutationTest.php
@@ -2,7 +2,7 @@
use WPGraphQL\Type\WPEnumType;
-class CheckoutMutationsTest extends \Codeception\TestCase\WPTestCase {
+class CheckoutMutationTest extends \Codeception\TestCase\WPTestCase {
public function setUp() {
// before
parent::setUp();
@@ -26,7 +26,7 @@ class CheckoutMutationsTest extends \Codeception\TestCase\WPTestCase {
update_option( 'woocommerce_calc_taxes', 'yes' );
update_option( 'woocommerce_tax_round_at_subtotal', 'no' );
- // Enable payment gateway.
+ // Enable payment gateways.
update_option(
'woocommerce_bacs_settings',
array(
@@ -38,6 +38,32 @@ class CheckoutMutationsTest extends \Codeception\TestCase\WPTestCase {
)
);
+ update_option(
+ 'woocommerce_stripe_settings',
+ array(
+ 'enabled' => 'yes',
+ 'title' => 'Credit Card (Stripe)',
+ 'description' => 'Pay with your credit card via Stripe',
+ 'webhook' => '',
+ 'testmode' => 'yes',
+ 'test_publishable_key' => STRIPE_API_PUBLISHABLE_KEY,
+ 'test_secret_key' => STRIPE_API_SECRET_KEY,
+ 'test_webhook_secret' => '',
+ 'publishable_key' => '',
+ 'secret_key' => '',
+ 'webhook_secret' => '',
+ 'inline_cc_form' => 'no',
+ 'statement_descriptor' => '',
+ 'capture' => 'yes',
+ 'payment_request' => 'yes',
+ 'payment_request_button_type' => 'buy',
+ 'payment_request_button_theme' => 'dark',
+ 'payment_request_button_height' => '44',
+ 'saved_cards' => 'yes',
+ 'logging' => 'no',
+ )
+ );
+
// Additional cart fees.
add_action(
'woocommerce_cart_calculate_fees',
@@ -76,176 +102,178 @@ class CheckoutMutationsTest extends \Codeception\TestCase\WPTestCase {
parent::tearDown();
}
- private function checkout( $input ) {
- $mutation = '
- mutation checkout( $input: CheckoutInput! ) {
- checkout( input: $input ) {
- clientMutationId
- order {
- id
- orderId
- currency
- orderVersion
- date
- modified
- status
- discountTotal
- discountTax
- shippingTotal
- shippingTax
- cartTax
- total
- totalTax
- subtotal
- orderNumber
- orderKey
- createdVia
- pricesIncludeTax
- parent {
+ private function checkout( $input, $mutation = null ) {
+ if ( ! $mutation ) {
+ $mutation = '
+ mutation checkout( $input: CheckoutInput! ) {
+ checkout( input: $input ) {
+ clientMutationId
+ order {
id
+ orderId
+ currency
+ orderVersion
+ date
+ modified
+ status
+ discountTotal
+ discountTax
+ shippingTotal
+ shippingTax
+ cartTax
+ total
+ totalTax
+ subtotal
+ orderNumber
+ orderKey
+ createdVia
+ pricesIncludeTax
+ parent {
+ id
+ }
+ customer {
+ id
+ }
+ customerIpAddress
+ customerUserAgent
+ customerNote
+ billing {
+ firstName
+ lastName
+ company
+ address1
+ address2
+ city
+ state
+ postcode
+ country
+ email
+ phone
+ }
+ shipping {
+ firstName
+ lastName
+ company
+ address1
+ address2
+ city
+ state
+ postcode
+ country
+ }
+ paymentMethod
+ paymentMethodTitle
+ transactionId
+ dateCompleted
+ datePaid
+ cartHash
+ shippingAddressMapUrl
+ hasBillingAddress
+ hasShippingAddress
+ isDownloadPermitted
+ needsShippingAddress
+ hasDownloadableItem
+ downloadableItems {
+ nodes {
+ url
+ accessExpires
+ downloadId
+ downloadsRemaining
+ name
+ product {
+ productId
+ }
+ download {
+ downloadId
+ }
+ }
+ }
+ needsPayment
+ needsProcessing
+ metaData {
+ key
+ value
+ }
+ couponLines {
+ nodes {
+ itemId
+ orderId
+ code
+ discount
+ discountTax
+ coupon {
+ id
+ }
+ }
+ }
+ feeLines {
+ nodes {
+ itemId
+ orderId
+ amount
+ name
+ taxStatus
+ total
+ totalTax
+ taxClass
+ }
+ }
+ shippingLines {
+ nodes {
+ itemId
+ orderId
+ methodTitle
+ total
+ totalTax
+ taxClass
+ }
+ }
+ taxLines {
+ nodes {
+ rateCode
+ label
+ taxTotal
+ shippingTaxTotal
+ isCompound
+ taxRate {
+ rateId
+ }
+ }
+ }
+ lineItems {
+ nodes {
+ productId
+ variationId
+ quantity
+ taxClass
+ subtotal
+ subtotalTax
+ total
+ totalTax
+ taxStatus
+ product {
+ ... on SimpleProduct {
+ id
+ }
+ ... on VariableProduct {
+ id
+ }
+ }
+ variation {
+ id
+ }
+ }
+ }
}
customer {
id
}
- customerIpAddress
- customerUserAgent
- customerNote
- billing {
- firstName
- lastName
- company
- address1
- address2
- city
- state
- postcode
- country
- email
- phone
- }
- shipping {
- firstName
- lastName
- company
- address1
- address2
- city
- state
- postcode
- country
- }
- paymentMethod
- paymentMethodTitle
- transactionId
- dateCompleted
- datePaid
- cartHash
- shippingAddressMapUrl
- hasBillingAddress
- hasShippingAddress
- isDownloadPermitted
- needsShippingAddress
- hasDownloadableItem
- downloadableItems {
- nodes {
- url
- accessExpires
- downloadId
- downloadsRemaining
- name
- product {
- productId
- }
- download {
- downloadId
- }
- }
- }
- needsPayment
- needsProcessing
- metaData {
- key
- value
- }
- couponLines {
- nodes {
- itemId
- orderId
- code
- discount
- discountTax
- coupon {
- id
- }
- }
- }
- feeLines {
- nodes {
- itemId
- orderId
- amount
- name
- taxStatus
- total
- totalTax
- taxClass
- }
- }
- shippingLines {
- nodes {
- itemId
- orderId
- methodTitle
- total
- totalTax
- taxClass
- }
- }
- taxLines {
- nodes {
- rateCode
- label
- taxTotal
- shippingTaxTotal
- isCompound
- taxRate {
- rateId
- }
- }
- }
- lineItems {
- nodes {
- productId
- variationId
- quantity
- taxClass
- subtotal
- subtotalTax
- total
- totalTax
- taxStatus
- product {
- ... on SimpleProduct {
- id
- }
- ... on VariableProduct {
- id
- }
- }
- variation {
- id
- }
- }
- }
+ result
+ redirect
}
- customer {
- id
- }
- result
- redirect
}
- }
- ';
+ ';
+ }
$actual = graphql(
array(
@@ -258,8 +286,46 @@ class CheckoutMutationsTest extends \Codeception\TestCase\WPTestCase {
return $actual;
}
+ private function create_stripe_customer( $email ) {
+ $customer = \Stripe\Customer::create( array( 'email' => $email ) );
+
+ // use --debug flag to view.
+ codecept_debug( $customer );
+
+ return $customer;
+ }
+
+ private function create_stripe_source( $customer ) {
+ $source = \Stripe\Customer::createSource(
+ $customer['id'],
+ array( 'source' => 'tok_visa')
+ );
+
+ // use --debug flag to view.
+ codecept_debug( $source );
+
+ return $source;
+ }
+
+ private function create_stripe_payment_intent( $amount, $customer ) {
+ $payment_intent = \Stripe\PaymentIntent::create(
+ array(
+ 'amount' => $amount,
+ 'currency' => 'gbp',
+ 'payment_method_types' => ['card'],
+ 'customer' => $customer['id'],
+ 'payment_method' => $customer['invoice_settings']['default_payment_method'],
+ )
+ );
+
+ // use --debug flag to view.
+ codecept_debug( $payment_intent );
+
+ return $payment_intent;
+ }
+
// tests
- public function testCheckoutOrderMutation() {
+ public function testCheckoutMutation() {
wp_set_current_user( $this->simple_customer );
$variable = $this->variation->create( $this->product->create_variable() );
$product_ids = array(
@@ -466,7 +532,7 @@ class CheckoutMutationsTest extends \Codeception\TestCase\WPTestCase {
$this->assertEquals( $expected, $actual );
}
- public function testCheckoutOrderMutationWithNewAccount() {
+ public function testCheckoutMutationWithNewAccount() {
$variable = $this->variation->create( $this->product->create_variable() );
$product_ids = array(
$this->product->create_simple(),
@@ -657,7 +723,7 @@ class CheckoutMutationsTest extends \Codeception\TestCase\WPTestCase {
$this->assertEquals( $expected, $actual );
}
- public function testCheckoutOrderMutationWithNoAccount() {
+ public function testCheckoutMutationWithNoAccount() {
update_option( 'woocommerce_enable_guest_checkout', 'yes' );
$variable = $this->variation->create( $this->product->create_variable() );
$product_ids = array(
@@ -852,7 +918,7 @@ class CheckoutMutationsTest extends \Codeception\TestCase\WPTestCase {
$this->assertEquals( $expected, $actual );
}
- public function testCheckoutOrderMutationWithPrepaidOrder() {
+ public function testCheckoutMutationWithPrepaidOrder() {
update_option( 'woocommerce_enable_guest_checkout', 'yes' );
$product_ids = array(
$this->product->create_simple(
@@ -1033,4 +1099,392 @@ class CheckoutMutationsTest extends \Codeception\TestCase\WPTestCase {
$this->assertEquals( $expected, $actual );
}
+
+ public function testCheckoutMutationWithStripe() {
+ wp_set_current_user( $this->simple_customer );
+ $variable = $this->variation->create( $this->product->create_variable() );
+ $product_ids = array(
+ $this->product->create_simple(),
+ $this->product->create_simple(),
+ $variable['product'],
+ );
+ $coupon = new WC_Coupon(
+ $this->coupon->create( array( 'product_ids' => $product_ids ) )
+ );
+ WC()->cart->add_to_cart( $product_ids[0], 3 );
+ WC()->cart->add_to_cart( $product_ids[1], 6 );
+ WC()->cart->add_to_cart( $product_ids[2], 2, $variable['variations'][0] );
+ WC()->cart->apply_coupon( $coupon->get_code() );
+
+ $amount = (int) floatval( WC()->cart->get_cart_contents_total() + WC()->cart->get_cart_contents_tax() ) * 100;
+ $stripe_customer = $this->create_stripe_customer( 'superfreak500@gmail.com' );
+ $stripe_source = $this->create_stripe_source( $stripe_customer );
+ $payment_intent = $this->create_stripe_payment_intent( $amount, $stripe_customer );
+
+ $input = array(
+ 'clientMutationId' => 'someId',
+ 'paymentMethod' => 'stripe',
+ 'shippingMethod' => 'flat rate',
+ 'billing' => array(
+ 'firstName' => 'May',
+ 'lastName' => 'Parker',
+ 'address1' => '20 Ingram St',
+ 'city' => 'New York City',
+ 'state' => 'NY',
+ 'postcode' => '12345',
+ 'country' => 'US',
+ 'email' => 'superfreak500@gmail.com',
+ 'phone' => '555-555-1234',
+ ),
+ 'shipping' => array(
+ 'firstName' => 'May',
+ 'lastName' => 'Parker',
+ 'address1' => '20 Ingram St',
+ 'city' => 'New York City',
+ 'state' => 'NY',
+ 'postcode' => '12345',
+ 'country' => 'US',
+ ),
+ 'metaData' => array(
+ array(
+ 'key' => '_stripe_source_id',
+ 'value' => $stripe_source['id'],
+ ),
+ array(
+ 'key' => '_stripe_customer_id',
+ 'value' => $stripe_customer['id'],
+ ),
+ array(
+ 'key' => '_stripe_intent_id',
+ 'value' => $payment_intent['id'],
+ ),
+ )
+ );
+
+ // Remove "metaData" value field and "redirect" link from the mutation output.
+ $modified_mutation = '
+ mutation checkout( $input: CheckoutInput! ) {
+ checkout( input: $input ) {
+ clientMutationId
+ order {
+ id
+ orderId
+ currency
+ orderVersion
+ date
+ modified
+ status
+ discountTotal
+ discountTax
+ shippingTotal
+ shippingTax
+ cartTax
+ total
+ totalTax
+ subtotal
+ orderNumber
+ orderKey
+ createdVia
+ pricesIncludeTax
+ parent {
+ id
+ }
+ customer {
+ id
+ }
+ customerIpAddress
+ customerUserAgent
+ customerNote
+ billing {
+ firstName
+ lastName
+ company
+ address1
+ address2
+ city
+ state
+ postcode
+ country
+ email
+ phone
+ }
+ shipping {
+ firstName
+ lastName
+ company
+ address1
+ address2
+ city
+ state
+ postcode
+ country
+ }
+ paymentMethod
+ paymentMethodTitle
+ transactionId
+ dateCompleted
+ datePaid
+ cartHash
+ shippingAddressMapUrl
+ hasBillingAddress
+ hasShippingAddress
+ isDownloadPermitted
+ needsShippingAddress
+ hasDownloadableItem
+ downloadableItems {
+ nodes {
+ url
+ accessExpires
+ downloadId
+ downloadsRemaining
+ name
+ product {
+ productId
+ }
+ download {
+ downloadId
+ }
+ }
+ }
+ needsPayment
+ needsProcessing
+ metaData {
+ key
+ }
+ couponLines {
+ nodes {
+ itemId
+ orderId
+ code
+ discount
+ discountTax
+ coupon {
+ id
+ }
+ }
+ }
+ feeLines {
+ nodes {
+ itemId
+ orderId
+ amount
+ name
+ taxStatus
+ total
+ totalTax
+ taxClass
+ }
+ }
+ shippingLines {
+ nodes {
+ itemId
+ orderId
+ methodTitle
+ total
+ totalTax
+ taxClass
+ }
+ }
+ taxLines {
+ nodes {
+ rateCode
+ label
+ taxTotal
+ shippingTaxTotal
+ isCompound
+ taxRate {
+ rateId
+ }
+ }
+ }
+ lineItems {
+ nodes {
+ productId
+ variationId
+ quantity
+ taxClass
+ subtotal
+ subtotalTax
+ total
+ totalTax
+ taxStatus
+ product {
+ ... on SimpleProduct {
+ id
+ }
+ ... on VariableProduct {
+ id
+ }
+ }
+ variation {
+ id
+ }
+ }
+ }
+ }
+ customer {
+ id
+ }
+ result
+ }
+ }
+ ';
+
+ /**
+ * Assertion One
+ *
+ * Test mutation and input.
+ */
+ $actual = $this->checkout( $input, $modified_mutation );
+
+ // use --debug flag to view.
+ codecept_debug( $actual );
+
+ $this->assertArrayHasKey('data', $actual );
+ $this->assertArrayHasKey('checkout', $actual['data'] );
+ $this->assertArrayHasKey('order', $actual['data']['checkout'] );
+ $this->assertArrayHasKey('id', $actual['data']['checkout']['order'] );
+ $order = \WC_Order_Factory::get_order( $actual['data']['checkout']['order']['orderId'] );
+
+ // Get Available payment gateways.
+ $available_gateways = WC()->payment_gateways->get_available_payment_gateways();
+
+ $expected = array(
+ 'data' => array(
+ 'checkout' => array(
+ 'clientMutationId' => 'someId',
+ 'order' => array_merge(
+ $this->order->print_query( $order->get_id() ),
+ array(
+ 'metaData' => array(
+ array( 'key' => 'is_vat_exempt', ),
+ array( 'key' => '_stripe_source_id' ),
+ array( 'key' => '_stripe_customer_id' ),
+ array( 'key' => '_stripe_intent_id' ),
+ array( 'key' => '_stripe_charge_captured' ),
+ array( 'key' => '_stripe_fee' ),
+ array( 'key' => '_stripe_net' ),
+ array( 'key' => '_stripe_currency' ),
+ ),
+ 'couponLines' => array(
+ 'nodes' => array_reverse(
+ array_map(
+ function( $item ) {
+ return array(
+ 'itemId' => $item->get_id(),
+ 'orderId' => $item->get_order_id(),
+ 'code' => $item->get_code(),
+ 'discount' => ! empty( $item->get_discount() ) ? $item->get_discount() : null,
+ 'discountTax' => ! empty( $item->get_discount_tax() ) ? $item->get_discount_tax() : null,
+ 'coupon' => null,
+ );
+ },
+ $order->get_items( 'coupon' )
+ )
+ ),
+ ),
+ 'feeLines' => array(
+ 'nodes' => array_reverse(
+ array_map(
+ function( $item ) {
+ return array(
+ 'itemId' => $item->get_id(),
+ 'orderId' => $item->get_order_id(),
+ 'amount' => $item->get_amount(),
+ 'name' => $item->get_name(),
+ 'taxStatus' => strtoupper( $item->get_tax_status() ),
+ 'total' => $item->get_total(),
+ 'totalTax' => ! empty( $item->get_total_tax() ) ? $item->get_total_tax() : null,
+ 'taxClass' => ! empty( $item->get_tax_class() )
+ ? WPEnumType::get_safe_name( $item->get_tax_class() )
+ : 'STANDARD',
+ );
+ },
+ $order->get_items( 'fee' )
+ )
+ ),
+ ),
+ 'shippingLines' => array(
+ 'nodes' => array_reverse(
+ array_map(
+ function( $item ) {
+
+ return array(
+ 'itemId' => $item->get_id(),
+ 'orderId' => $item->get_order_id(),
+ 'methodTitle' => $item->get_method_title(),
+ 'total' => $item->get_total(),
+ 'totalTax' => !empty( $item->get_total_tax() )
+ ? $item->get_total_tax()
+ : null,
+ 'taxClass' => ! empty( $item->get_tax_class() )
+ ? $item->get_tax_class() === 'inherit'
+ ? WPEnumType::get_safe_name( 'inherit cart' )
+ : WPEnumType::get_safe_name( $item->get_tax_class() )
+ : 'STANDARD'
+ );
+ },
+ $order->get_items( 'shipping' )
+ )
+ ),
+ ),
+ 'taxLines' => array(
+ 'nodes' => array_reverse(
+ array_map(
+ function( $item ) {
+ return array(
+ 'rateCode' => $item->get_rate_code(),
+ 'label' => $item->get_label(),
+ 'taxTotal' => $item->get_tax_total(),
+ 'shippingTaxTotal' => $item->get_shipping_tax_total(),
+ 'isCompound' => $item->is_compound(),
+ 'taxRate' => array( 'rateId' => $item->get_rate_id() ),
+ );
+ },
+ $order->get_items( 'tax' )
+ )
+ ),
+ ),
+ 'lineItems' => array(
+ 'nodes' => array_values(
+ array_map(
+ function( $item ) {
+ return array(
+ 'productId' => $item->get_product_id(),
+ 'variationId' => ! empty( $item->get_variation_id() )
+ ? $item->get_variation_id()
+ : null,
+ 'quantity' => $item->get_quantity(),
+ 'taxClass' => ! empty( $item->get_tax_class() )
+ ? strtoupper( $item->get_tax_class() )
+ : 'STANDARD',
+ 'subtotal' => ! empty( $item->get_subtotal() ) ? $item->get_subtotal() : null,
+ 'subtotalTax' => ! empty( $item->get_subtotal_tax() ) ? $item->get_subtotal_tax() : null,
+ 'total' => ! empty( $item->get_total() ) ? $item->get_total() : null,
+ 'totalTax' => ! empty( $item->get_total_tax() ) ? $item->get_total_tax() : null,
+ 'taxStatus' => strtoupper( $item->get_tax_status() ),
+ 'product' => array( 'id' => $this->product->to_relay_id( $item->get_product_id() ) ),
+ 'variation' => ! empty( $item->get_variation_id() )
+ ? array(
+ 'id' => $this->variation->to_relay_id( $item->get_variation_id() )
+ )
+ : null,
+ );
+ },
+ $order->get_items()
+ )
+ ),
+ ),
+ )
+ ),
+ 'customer' => array(
+ 'id' => $this->customer->to_relay_id( $order->get_customer_id() )
+ ),
+ 'result' => 'success',
+ ),
+ )
+ );
+
+ $this->assertEquals( $expected, $actual );
+ }
}
\ No newline at end of file