Преглед изворни кода

docs: Add customer account guide

Michael Bromley пре 2 година
родитељ
комит
679c110263

+ 428 - 0
docs/docs/guides/storefront/checkout-flow/index.mdx

@@ -0,0 +1,428 @@
+---
+title: "Checkout Flow"
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+Once the customer has added the desired products to the active order, it's time to check out.
+
+This guide assumes that you are using the [default OrderProcess](/guides/core-concepts/orders/#the-order-process), so
+if you have defined a custom process, some of these steps may be slightly different.
+
+:::note
+In this guide, we will assume that an `ActiveOrder` fragment has been defined, as detailed in the
+[Managing the Active Order guide](/guides/storefront/active-order/#define-an-order-fragment), but for the purposes of
+checking out the fragment should also include `customer` `shippingAddress` and `billingAddress` fields.
+:::
+
+## Add a customer
+
+Every order must be associated with a customer. If the customer is not logged in, then the [`setCustomerForOrder`](/reference/graphql-api/shop/mutations/#setcustomerfororder) mutation must be called. This will create a new Customer record if the provided email address does not already exist in the database.
+
+:::note
+If the customer is already logged in, then this step is skipped.
+:::
+
+<Tabs>
+<TabItem value="Mutation" label="Mutation" default>
+
+```graphql
+mutation SetCustomerForOrder($input: CreateCustomerInput!) {
+  setCustomerForOrder(input: $input) {
+    ...ActiveOrder
+    ...on ErrorResult {
+      errorCode
+      message
+    }
+  }
+}
+```
+
+</TabItem>
+<TabItem value="Variables" label="Variables">
+
+```json
+{
+  "input": {
+    "title": "Mr.",
+    "firstName": "Bob",
+    "lastName": "Dobalina",
+    "phoneNumber": "1234556",
+    "emailAddress": "b.dobalina@email.com",
+  }
+}
+```
+
+</TabItem>
+</Tabs>
+
+## Set the shipping address
+
+The [`setOrderShippingAddress`](/reference/graphql-api/shop/mutations/#setordershippingaddress) mutation must be called to set the shipping address for the order.
+
+
+<Tabs>
+<TabItem value="Mutation" label="Mutation" default>
+
+```graphql
+mutation SetOrderShippingAddress($input: CreateAddressInput!) {
+  setOrderShippingAddress(input: $input) {
+    ...ActiveOrder
+    ...on ErrorResult {
+      errorCode
+      message
+    }
+  }
+}
+```
+
+</TabItem>
+<TabItem value="Variables" label="Variables">
+
+```json
+{
+  "input": {
+    "fullName": "John Doe",
+    "company": "ABC Inc.",
+    "streetLine1": "123 Main St",
+    "streetLine2": "Apt 4B",
+    "city": "New York",
+    "province": "NY",
+    "postalCode": "10001",
+    "countryCode": "US",
+    "phoneNumber": "123-456-7890",
+    "defaultShippingAddress": true,
+    "defaultBillingAddress": false
+  }
+}
+```
+
+</TabItem>
+</Tabs>
+
+If the customer is logged in, you can check their existing addresses and pre-populate an address form if an existing address is found.
+
+
+<Tabs>
+<TabItem value="Query" label="Query" default>
+
+```graphql
+query GetCustomerAddresses {
+  activeCustomer {
+    id
+    addresses {
+      id
+      fullName
+      company
+      streetLine1
+      streetLine2
+      city
+      province
+      postalCode
+      country {
+        code
+        name
+      }
+      phoneNumber
+      defaultShippingAddress
+      defaultBillingAddress
+    }
+  }
+}
+```
+
+</TabItem>
+<TabItem value="Result" label="Result">
+
+```json
+{
+  "data": {
+    "activeCustomer": {
+      "id": "123456",
+      "addresses": [
+        {
+          "id": "123",
+          "fullName": "John Doe",
+          "company": "",
+          "streetLine1": "123 Main St",
+          "streetLine2": "Apt 4B",
+          "city": "New York",
+          "province": "NY",
+          "postalCode": "10001",
+          "country": {
+            "code": "US",
+            "name": "United States"
+          },
+          "phoneNumber": "123-456-7890",
+          "defaultShippingAddress": true,
+          "defaultBillingAddress": false
+        },
+        {
+          "id": "124",
+          "fullName": "John Doe",
+          "company": "",
+          "streetLine1": "456 Elm St",
+          "streetLine2": "",
+          "city": "Los Angeles",
+          "province": "CA",
+          "postalCode": "90001",
+          "country": {
+            "code": "US",
+            "name": "United States"
+          },
+          "phoneNumber": "987-654-3210",
+          "defaultShippingAddress": false,
+          "defaultBillingAddress": true
+        }
+      ]
+    }
+  }
+}
+```
+
+</TabItem>
+</Tabs>
+
+## Set the shipping method
+
+Now that we know the shipping address, we can check which shipping methods are available with the [`eligibleShippingMethods`](/reference/graphql-api/shop/queries/#eligibleshippingmethods) query.
+
+<Tabs>
+<TabItem value="Query" label="Query" default>
+
+```graphql
+query GetShippingMethods {
+  eligibleShippingMethods {
+    id
+    price
+    description
+  }
+}
+```
+
+</TabItem>
+<TabItem value="Result" label="Result">
+
+```json
+{
+  "data": {
+    "eligibleShippingMethods": [
+      {
+        "id": "1",
+        "price": 545,
+        "description": "Standard Shipping"
+      },
+      {
+        "id": "2",
+        "price": 1250,
+        "description": "Expedited Shipping"
+      },
+      {
+        "id": "3",
+        "price": 1695,
+        "description": "Overnight Shipping"
+      }
+    ]
+  }
+}
+```
+
+</TabItem>
+</Tabs>
+
+The results can then be displayed to the customer so they can choose the desired shipping method. If there is only a single
+result, then it can be automatically selected.
+
+The desired shipping method's id is the passed to the [`setOrderShippingMethod`](/reference/graphql-api/shop/mutations/#setordershippingmethod) mutation.
+
+```graphql
+mutation SetShippingMethod($id: [ID!]!) {
+    setOrderShippingMethod(shippingMethodId: $id) {
+        ...ActiveOrder
+        ...on ErrorResult {
+            errorCode
+            message
+        }
+    }
+}
+```
+
+## Add payment
+
+The [`eligiblePaymentMethods`](/reference/graphql-api/shop/queries/#eligiblepaymentmethods) query can be used to get a list of available payment methods.
+This list can then be displayed to the customer, so they can choose the desired payment method.
+
+
+<Tabs>
+<TabItem value="Query" label="Query" default>
+
+```graphql
+query GetPaymentMethods {
+  eligiblePaymentMethods {
+    id
+    name
+    code
+    isEligible
+  }
+}
+```
+
+</TabItem>
+<TabItem value="Result" label="Result">
+
+```json
+{
+  "data": {
+    "eligiblePaymentMethods": [
+      {
+        "id": "1",
+        "name": "Stripe",
+        "code": "stripe",
+        "isEligible": true
+      },
+      {
+        "id": "2",
+        "name": "Apple Pay",
+        "code": "apple-pay",
+        "isEligible": true
+      }
+      {
+        "id": "3",
+        "name": "Pay on delivery",
+        "code": "pay-on-delivery",
+        "isEligible": false
+      }
+    ]
+  }
+}
+```
+
+</TabItem>
+</Tabs>
+
+Next, we need to transition the order to the `ArrangingPayment` state. This state ensures that no other changes can be made to the order
+while the payment is being arranged. The [`transitionOrderToState`](/reference/graphql-api/shop/mutations/#transitionordertostate) mutation is used to transition the order to the `ArrangingPayment` state.
+
+<Tabs>
+<TabItem value="Query" label="Query" default>
+
+```graphql
+mutation TransitionToState($state: String!)
+  transitionOrderToState(state: $state) {
+    ...ActiveOrder
+    ...on OrderStateTransitionError {
+      errorCode
+      message
+      transitionError
+      fromState
+      toState
+    }
+  }
+}
+```
+
+</TabItem>
+<TabItem value="Variables" label="Variables">
+
+```json
+{
+  "state": "ArrangingPayment"
+}
+```
+
+</TabItem>
+</Tabs>
+
+At this point, your storefront will use an integration with the payment provider to collect the customer's payment details, and then
+the exact sequence of API calls will depend on the payment integration.
+
+The [`addPaymentToOrder`](/reference/graphql-api/shop/mutations/#addpaymenttoorder) mutation is the general-purpose mutation for adding a payment to an order.
+It accepts a `method` argument which must corresponde to the `code` of the selected payment method, and a `metadata` argument which is a JSON object containing any additional information required by that particular integration.
+
+For example, the demo data populated in a new Vendure installation includes a "Standard Payment" method, which uses the [`dummyPaymentHandler`](/reference/typescript-api/payment/dummy-payment-handler) to simulate a payment provider. Here's how you would add a payment using this method:
+
+
+<Tabs>
+<TabItem value="Mutation" label="Mutation" default>
+
+```graphql
+mutation AddPaymentToOrder($input: PaymentInput!) {
+  addPaymentToOrder(input: $input) {
+    ...ActiveOrder
+    ...on ErrorResult {
+      errorCode
+      message
+    }
+  }
+}
+```
+
+</TabItem>
+<TabItem value="Variables" label="Variables">
+
+```json
+{
+  "method": "standard-payment",
+  "metadata": {
+    "shouldDecline": false,
+    "shouldError": false,
+    "shouldErrorOnSettle": false,
+  }
+}
+```
+
+</TabItem>
+</Tabs>
+
+Other payment integrations have specific setup instructions you must follow:
+
+### Stripe
+
+Our [`StripePlugin docs`](/reference/typescript-api/core-plugins/payments-plugin/stripe-plugin/) describe how to set up your checkout to use Stripe.
+
+### Braintree
+
+Our [`BraintreePlugin` docs](/reference/typescript-api/core-plugins/payments-plugin/braintree-plugin/) describe how to set up your checkout to use Braintree.
+
+### Mollie
+
+Our [`MolliePlugin` docs](/reference/typescript-api/core-plugins/payments-plugin/mollie-plugin/) describe how to set up your checkout to use Mollie.
+
+### Other payment providers
+
+For more information on how to integrate with a payment provider, see the [Payment](/guides/core-concepts/payment/) guide.
+
+## Display confirmation
+
+Once the checkout has completed, the order will no longer be considered "active" (see the [`OrderPlacedStrategy`](/reference/typescript-api/orders/order-placed-strategy/)) and so the [`activeOrder`](/reference/graphql-api/shop/queries/#activeorder) query will return `null`. Instead, the [`orderByCode`](/reference/graphql-api/shop/queries/#orderbycode) query can be used to retrieve the order by its code to display a confirmation page.
+
+<Tabs>
+<TabItem value="Query" label="Query" default>
+
+```graphql
+query GetOrderByCode($code: String!) {
+  orderByCode(code: $code) {
+    ...Order
+  }
+}
+```
+
+</TabItem>
+<TabItem value="Variables" label="Variables">
+
+```json
+{
+  "code": "PJGY46GCB1EDU9YH"
+}
+```
+
+</TabItem>
+</Tabs>
+
+:::info
+By default Vendure will only allow access to the order by code for the first 2 hours after the order is placed if the customer is not logged in.
+This is to prevent a malicious user from guessing order codes and viewing other customers' orders. This can be configured via the [`OrderByCodeAccessStrategy`](/reference/typescript-api/orders/order-by-code-access-strategy/).
+:::
+
+

+ 0 - 10
docs/docs/guides/storefront/configuring-a-graphql-client.md

@@ -1,10 +0,0 @@
----
-title: "Configuring a GraphQL Client"
-weight: 2
-showtoc: true
----
-
-# Configuring a GraphQL Client
-
-This guide provides examples of how to set up popular GraphQL clients to work with the Vendure Shop API. These examples are designed to work with both the `bearer` and `cookie` methods of [managing sessions]({{< relref "managing-sessions" >}}).
-

+ 0 - 77
docs/docs/guides/storefront/managing-sessions.md

@@ -1,77 +0,0 @@
----
-title: "Managing Sessions"
-weight: 1
-showtoc: true
----
- 
-# Managing Sessions
-
-Vendure supports two ways to manage user sessions: cookies and bearer token. The method you choose depends on your requirements, and is specified by the [`authOptions.tokenMethod` property]({{< relref "auth-options" >}}#tokenmethod) of the VendureConfig.
-
-## Cookie-based sessions (default)
-
-Using cookies is the simpler approach for browser-based applications, since the browser will manage the cookies for you automatically. 
-
-1. Enable the `credentials` option in your HTTP client. This allows the browser to send the session cookie with each request. 
-
-    For example, if using a fetch-based client (such as [Apollo client](https://www.apollographql.com/docs/react/recipes/authentication/#cookie)) you would set `credentials: 'include'` or if using [XMLHttpRequest](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/withCredentials), you would set `withCredentials: true`
-
-2. When using cookie-based sessions, you should set the [`authOptions.cookieOptions.secret` property]({{< relref "cookie-options" >}}#secret) to some secret string which will be used to sign the cookies sent to clients to prevent tampering. This string could be hard-coded in your config file, or (better) reside in an environment variable:
-
-```ts
-const config = {
-  // ...
-  authOptions: {
-    tokenMethod: 'cookie',
-    cookieOptions: {
-      secret: process.env.COOKIE_SESSION_SECRET
-    }
-  }
-}
-```
-
-## Bearer-token sessions
-
-In environments where cookies cannot be easily used (e.g. in some server environments or mobile apps), then the bearer-token method can be used.
-
-Using bearer tokens involves a bit more work on your part: you'll need to manually read response headers to get the token, and once you have it you'll have to manually add it to the headers of each request. 
-
-The workflow would be as follows:
-
-1. Certain mutations and queries initiate a session (e.g. logging in, adding an item to an order etc.). When this happens, the response will contain a HTTP header which [by default is called `'vendure-auth-token'`]({{< relref "auth-options" >}}#authtokenheaderkey).
-2. So your http client would need to check for the presence of this header each time it receives a response from the server.
-3. If the `'vendure-auth-token'` header is present, read the value and store it because this is your bearer token.
-4. Attach this bearer token to each subsequent request as `Authorization: Bearer <token>`.
-
-Here's a simplified example of how that would look:
-
-```ts
-const config = {
-    // ...
-    authOptions: {
-        tokenMethod: 'bearer',
-    }
-}
-```
-
-```ts
-let token: string;
-
-export async function request(query: string, variables: any) {
-     // If we already know the token, set the Authorization header.
-     const headers = token ? { Authorization: `Bearer ${token}` } : {};
-     
-     const response = await someGraphQlClient(query, variables, headers);
-    
-     // Check the response headers to see if Vendure has set the 
-     // auth token. The header key "vendure-auth-token" may be set to
-     // a custom value with the authOptions.authTokenHeaderKey config option.
-     const authToken = response.headers.get('vendure-auth-token');
-     if (authToken != null) {
-         token = authToken;
-     }
-     return response.data;
-}
-```
-
-Real-world examples with specific clients can be found in the [Configuring a GraphQL Client guide]({{< relref "configuring-a-graphql-client" >}}).

+ 0 - 117
docs/docs/guides/storefront/shop-api-guide.md

@@ -1,117 +0,0 @@
----
-title: "Shop API Guide"
-weight: 2
-showtoc: true
----
-
-# GraphQL Shop API Guide
-
-This is an overview of the GraphQL Shop API, which is used when implementing a storefront application with Vendure. 
-
-{{< alert "warning" >}}
-This guide only lists some of the more common operations you'll need for your storefront. Please consult [the Shop API reference]({{< relref "/reference/graphql-api/shop" >}}) for a complete guide.
-{{< /alert >}}
-
-## Universal Parameters
-
-There are a couple of query parameters that are valid for all GraphQL operations:
-
-* `languageCode`: This sets the current language for the request. Any translatable types (e.g. Products, Facets, Collections) will be returned in that language, if a translation is defined for that language. If not, they will fall back to the default language. The value should be one of the ISO 639-1 codes defined by the [`LanguageCode` enum]({{< relref "language-code" >}}).
-
-  ```text
-  POST http://localhost:3000/shop-api?languageCode=de
-  ```
-* `vendure-token`: If your Vendure instance features more than a single [Channel]({{< relref "/guides/developer-guide/channels" >}}), the token of the active Channel can be specified by token as either a query parameter _or_ as a header. The name of the key can be configured by the [`channelTokenKey` config option]({{< relref "vendure-config" >}}#channeltokenkey).
-
-## Browsing the catalogue
-
-### Listing collections
-
-{{< shop-api-operation operation="collections" type="query" >}}
-
-This query lists available Collections. Useful for creating navigation menus. The query will return Collections as a flat list, rather than a tree structure. Using the `parent` and `children` fields, you can convert this list into an [`arrayToTree` function](https://github.com/vendure-ecommerce/storefront/blob/8848e9e0540c12e0eb964a90ca8accabccb4fbfa/src/app/core/components/collections-menu/array-to-tree.ts).
-
-### Listing products
-
-{{< shop-api-operation operation="search" type="query" >}}
-
-The `search` query is intended to be used for keyword searching, facet-based filtering, and for listing product results by collection. In practice this query can power all kinds of storefront product lists, and is preferable to the `products` query, since it is backed by a dedicated search index and as such, queries are significantly more efficient.
-
-### Filtering by facet
-
-A common requirement is to make product listing pages filterable based on facets. Using the `search` query, you can select a summary of all Facets and FacetValues which apply to the current results:
-
-```graphql
-query SearchProducts {
-  search(input: {
-    collectionSlug: "shoes"
-  }) {
-    totalItems
-    items {
-      productId
-      productName
-    }
-    facetValues {
-      facetValue {
-        id
-        name
-      }
-      count
-    }
-  }
-}
-```
-The FacetValue data can be then used to create a facet filter UI, allowing the customer to select which facets to filter for. The result set can then be filtered using the `facetValueFilters` input field:
-
-```graphql
-query SearchProducts {
-  search(input: {
-    collectionSlug: "shoes"
-    facetValueFilters: [
-      { or: [34, 25] },
-      { and: 12 }
-    ]
-  }) {
-    totalItems
-    # etc.
-  }
-}
-```
-
-The above example translates to **return all products in the "shoes" collection, which have FacetValue 12 AND (34 OR 25)**
-
-### Product detail
-
-{{< shop-api-operation operation="product" type="query" >}}
-
-Use the `product` query for the Product detail view.
-
-## Order flow
-
-*See the [Order Workflow guide]({{< relref "order-workflow" >}}) for a detailed explanation with examples of how Orders are handled in Vendure.*
-
-* {{< shop-api-operation operation="activeOrder" type="query" >}} returns the currently-active Order for the session.
-* {{< shop-api-operation operation="addItemToOrder" type="mutation" >}} adds an item to the order. The first time it is called will also create a new Order for the session.
-* {{< shop-api-operation operation="adjustOrderLine" type="mutation" >}} is used to adjust the quantity of an OrderLine.
-* {{< shop-api-operation operation="removeOrderLine" type="mutation" >}} removes an OrderLine from the Order.
-* {{< shop-api-operation operation="removeAllOrderLines" type="mutation" >}} removes all OrderLine from the Order.
-* {{< shop-api-operation operation="setCustomerForOrder" type="mutation" >}} specifies the Customer details (required if the customer is not logged in as an authenticated user).
-* {{< shop-api-operation operation="setOrderShippingAddress" type="mutation" >}} sets the shipping address for the Order.
-* {{< shop-api-operation operation="eligibleShippingMethods" type="query" >}} returns all available shipping methods based on the customer's shipping address and the contents of the Order.
-* {{< shop-api-operation operation="setOrderShippingMethod" type="mutation" >}} sets the shipping method to use.
-* {{< shop-api-operation operation="eligiblePaymentMethods" type="query" >}} returns all available payment methods based on the contents of the Order.
-* {{< shop-api-operation operation="nextOrderStates" type="query" >}} returns the possible next states that the active Order can transition to
-* {{< shop-api-operation operation="transitionOrderToState" type="mutation" >}} transitions the active Order to the given state according to the [Order state machine]({{< relref "order-workflow" >}}).
-* {{< shop-api-operation operation="addPaymentToOrder" type="mutation" >}} adds a payment to the Order. If the payment amount equals the order total, then the Order will be transitioned to either the `PaymentAuthorized` or `PaymentSettled` state (depending on how the payment provider is configured) and the order process is complete from the customer's side.
-* {{< shop-api-operation operation="orderByCode" type="query" >}} allows even guest Customers to fetch the order they just placed for up to 2 hours after placing it. This is intended to be used to display an order confirmation page immediately after the order is completed.
-
-
-## Customer account management
-
-* {{< shop-api-operation operation="registerCustomerAccount" type="mutation" >}}: Creates a new Customer account.
-* {{< shop-api-operation operation="login" type="mutation" >}}: Log in with registered Customer credentials.
-* {{< shop-api-operation operation="logout" type="mutation" >}}: Log out from Customer account.
-* {{< shop-api-operation operation="activeCustomer" type="query" >}}: Returns the current logged-in Customer, or `null` if not logged in. This is useful for displaying the logged-in status in the storefront. The returned [`Customer`]({{< relref "/reference/graphql-api/shop/object-types" >}}#customer) type can also be used to query the Customer's Order history, list of Addresses and other personal details.
-* {{< shop-api-operation operation="requestPasswordReset" type="mutation" >}}: Use this to implement a "forgotten password" flow. This will trigger a password reset email to be sent.
-* {{< shop-api-operation operation="resetPassword" type="mutation" >}}: Use the token provided in the password reset email to set a new password.
-

+ 2 - 0
docs/sidebars.js

@@ -104,6 +104,8 @@ const sidebars = {
                 'guides/storefront/listing-products/index',
                 'guides/storefront/product-detail/index',
                 'guides/storefront/active-order/index',
+                'guides/storefront/checkout-flow/index',
+                'guides/storefront/customer-accounts/index',
             ],
         },
         {