diff --git a/framework/reactioncommerce/api/cart/handlers/add-item.ts b/framework/reactioncommerce/api/cart/handlers/add-item.ts index c8ccb36af..0dd22d1a0 100644 --- a/framework/reactioncommerce/api/cart/handlers/add-item.ts +++ b/framework/reactioncommerce/api/cart/handlers/add-item.ts @@ -6,14 +6,14 @@ import { import getCartCookie from '@framework/api/utils/get-cart-cookie' import { REACTION_ANONYMOUS_CART_TOKEN_COOKIE, - REACTION_CART_ID_COOKIE, + REACTION_ANONYMOUS_CART_ID_COOKIE, } from '@framework/const' const addItem: CartHandlers['addItem'] = async ({ req: { cookies: { [REACTION_ANONYMOUS_CART_TOKEN_COOKIE]: anonymousCartToken, - [REACTION_CART_ID_COOKIE]: cartId, + [REACTION_ANONYMOUS_CART_ID_COOKIE]: cartId, }, }, res, @@ -54,9 +54,13 @@ const addItem: CartHandlers['addItem'] = async ({ console.log('created cart', createdCart.data.createCart.cart) res.setHeader('Set-Cookie', [ - getCartCookie(config.cartCookie, createdCart.data.createCart.token, 999), getCartCookie( - config.cartIdCookie, + config.anonymousCartTokenCookie, + createdCart.data.createCart.token, + 999 + ), + getCartCookie( + config.anonymousCartIdCookie, createdCart.data.createCart.cart._id, 999 ), diff --git a/framework/reactioncommerce/api/cart/handlers/get-cart.ts b/framework/reactioncommerce/api/cart/handlers/get-cart.ts index ca8d1d08e..59ac9b86b 100644 --- a/framework/reactioncommerce/api/cart/handlers/get-cart.ts +++ b/framework/reactioncommerce/api/cart/handlers/get-cart.ts @@ -1,10 +1,14 @@ import type { Cart } from '../../../types' import type { CartHandlers } from '../' import getAnomymousCartQuery from '@framework/utils/queries/get-anonymous-cart' +import accountCartByAccountIdQuery from '@framework/utils/queries/account-cart-by-account-id' +import reconcileCartsMutation from '@framework/utils/mutations/reconcile-carts' import getCartCookie from '@framework/api/utils/get-cart-cookie' +import getViewerId from '@framework/customer/get-viewer-id' import { REACTION_ANONYMOUS_CART_TOKEN_COOKIE, - REACTION_CART_ID_COOKIE, + REACTION_ANONYMOUS_CART_ID_COOKIE, + REACTION_CUSTOMER_TOKEN_COOKIE, } from '@framework/const.ts' import { normalizeCart } from '@framework/utils' @@ -13,32 +17,83 @@ const getCart: CartHandlers['getCart'] = async ({ req, res, config }) => { const { cookies: { [REACTION_ANONYMOUS_CART_TOKEN_COOKIE]: anonymousCartToken, - [REACTION_CART_ID_COOKIE]: cartId, + [REACTION_ANONYMOUS_CART_ID_COOKIE]: anonymousCartId, + [REACTION_CUSTOMER_TOKEN_COOKIE]: reactionCustomerToken, }, } = req let normalizedCart - console.log('get-cart API') - console.log('anonymousCartToken', anonymousCartToken) - console.log('cartId', cartId) - console.log('shopId', config.shopId) - - if (cartId && anonymousCartToken) { + if (anonymousCartId && anonymousCartToken && reactionCustomerToken) { const { - data: { cart: rawCart }, + data: { + reconcileCarts: { cart: rawReconciledCart }, + }, + } = await config.fetch( + reconcileCartsMutation, + { + variables: { + input: { + anonymousCartId, + cartToken: anonymousCartToken, + shopId: config.shopId, + }, + }, + }, + { + headers: { + Authorization: `Bearer ${reactionCustomerToken}`, + }, + } + ) + + normalizedCart = normalizeCart(rawReconciledCart) + + // Clear the anonymous cart cookies, as we're now using an account-tied cart + res.setHeader('Set-Cookie', [ + getCartCookie(config.anonymousCartTokenCookie), + getCartCookie(config.anonymousCartIdCookie), + ]) + } else if (anonymousCartId && anonymousCartToken) { + const { + data: { cart: rawAnonymousCart }, } = await config.fetch(getAnomymousCartQuery, { variables: { - cartId, + cartId: anonymousCartId, cartToken: anonymousCartToken, }, }) - normalizedCart = normalizeCart(rawCart) + normalizedCart = normalizeCart(rawAnonymousCart) + } else if (reactionCustomerToken && !anonymousCartToken && !anonymousCartId) { + const accountId = await getViewerId({ + customerToken: reactionCustomerToken, + config, + }) + + const { + data: { cart: rawAccountCart }, + } = await config.fetch( + accountCartByAccountIdQuery, + { + variables: { + accountId, + shopId: config.shopId, + }, + }, + { + headers: { + Authorization: `Bearer ${reactionCustomerToken}`, + }, + } + ) + + normalizedCart = normalizeCart(rawAccountCart) } else { + // If there's no cart for now, return a dummy cart ID to keep Next Commerce happy res.setHeader( 'Set-Cookie', - getCartCookie(config.cartCookie, config.dummyEmptyCartId, 999) + getCartCookie(config.anonymousCartIdCookie, config.dummyEmptyCartId, 999) ) } diff --git a/framework/reactioncommerce/api/cart/index.ts b/framework/reactioncommerce/api/cart/index.ts index 7a2197ce7..0e8e02507 100644 --- a/framework/reactioncommerce/api/cart/index.ts +++ b/framework/reactioncommerce/api/cart/index.ts @@ -29,7 +29,7 @@ const cartApi: ReactionCommerceApiHandler = async ( if (!isAllowedMethod(req, res, METHODS)) return const { cookies } = req - const cartId = cookies[config.cartCookie] + const cartId = cookies[config.anonymousCartTokenCookie] try { // Return current cart info diff --git a/framework/reactioncommerce/api/index.ts b/framework/reactioncommerce/api/index.ts index 2e3c44683..80e2ee051 100644 --- a/framework/reactioncommerce/api/index.ts +++ b/framework/reactioncommerce/api/index.ts @@ -3,7 +3,7 @@ import type { CommerceAPIConfig } from '@commerce/api' import { API_URL, REACTION_ANONYMOUS_CART_TOKEN_COOKIE, - REACTION_CART_ID_COOKIE, + REACTION_ANONYMOUS_CART_ID_COOKIE, REACTION_EMPTY_DUMMY_CART_ID, REACTION_CUSTOMER_TOKEN_COOKIE, REACTION_COOKIE_EXPIRE, @@ -42,10 +42,10 @@ export class Config { const config = new Config({ locale: 'en-US', commerceUrl: API_URL, - cartCookie: REACTION_ANONYMOUS_CART_TOKEN_COOKIE, - cartIdCookie: REACTION_CART_ID_COOKIE, + anonymousCartTokenCookie: REACTION_ANONYMOUS_CART_TOKEN_COOKIE, + anonymousCartIdCookie: REACTION_ANONYMOUS_CART_ID_COOKIE, dummyEmptyCartId: REACTION_EMPTY_DUMMY_CART_ID, - cartCookieMaxAge: REACTION_COOKIE_EXPIRE, + anonymousCartTokenCookieMaxAge: REACTION_COOKIE_EXPIRE, fetch: fetchGraphqlApi, customerCookie: REACTION_CUSTOMER_TOKEN_COOKIE, shopId: SHOP_ID, diff --git a/framework/reactioncommerce/api/utils/create-api-handler.ts b/framework/reactioncommerce/api/utils/create-api-handler.ts index 92dd11af8..b01e04664 100644 --- a/framework/reactioncommerce/api/utils/create-api-handler.ts +++ b/framework/reactioncommerce/api/utils/create-api-handler.ts @@ -39,8 +39,6 @@ export default function createApiHandler< handlers: H, defaultOptions: Options ) { - console.log('next api handler', defaultOptions) - return function getApiHandler({ config, operations, diff --git a/framework/reactioncommerce/const.ts b/framework/reactioncommerce/const.ts index c0faf6752..ca7a2c09c 100644 --- a/framework/reactioncommerce/const.ts +++ b/framework/reactioncommerce/const.ts @@ -1,7 +1,7 @@ export const REACTION_ANONYMOUS_CART_TOKEN_COOKIE = 'reaction_anonymousCartToken' -export const REACTION_CART_ID_COOKIE = 'reaction_cartId' +export const REACTION_ANONYMOUS_CART_ID_COOKIE = 'reaction_cartId' export const REACTION_EMPTY_DUMMY_CART_ID = 'DUMMY_EMPTY_CART_ID' diff --git a/framework/reactioncommerce/customer/get-customer-id.ts b/framework/reactioncommerce/customer/get-customer-id.ts deleted file mode 100644 index 931615691..000000000 --- a/framework/reactioncommerce/customer/get-customer-id.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { getConfig, ReactionCommerceConfig } from '../api' -import getCustomerIdQuery from '../utils/queries/get-customer-id-query' -import Cookies from 'js-cookie' - -async function getCustomerId({ - customerToken: customerAccesToken, - config, -}: { - customerToken: string - config?: ReactionCommerceConfig -}): Promise { - config = getConfig(config) - - const { data } = await config.fetch(getCustomerIdQuery, { - variables: { - customerAccesToken: - customerAccesToken || Cookies.get(config.customerCookie), - }, - }) - - return data.customer?.id -} - -export default getCustomerId diff --git a/framework/reactioncommerce/customer/get-viewer-id.ts b/framework/reactioncommerce/customer/get-viewer-id.ts new file mode 100644 index 000000000..734f30831 --- /dev/null +++ b/framework/reactioncommerce/customer/get-viewer-id.ts @@ -0,0 +1,26 @@ +import { getConfig, ReactionCommerceConfig } from '../api' +import getViewerIdQuery from '../utils/queries/get-customer-id-query' + +async function getViewerId({ + customerToken: customerAccessToken, + config, +}: { + customerToken: string + config?: ReactionCommerceConfig +}): Promise { + config = getConfig(config) + + const { data } = await config.fetch( + getViewerIdQuery, + {}, + { + headers: { + Authorization: `Bearer ${customerAccessToken}`, + }, + } + ) + + return data.viewer?._id +} + +export default getViewerId diff --git a/framework/reactioncommerce/index.tsx b/framework/reactioncommerce/index.tsx index eb6fc8877..e18c619e4 100644 --- a/framework/reactioncommerce/index.tsx +++ b/framework/reactioncommerce/index.tsx @@ -15,7 +15,7 @@ export type { ReactionCommerceProvider } export const reactionCommerceConfig: CommerceConfig = { locale: 'en-us', - cartCookie: REACTION_ANONYMOUS_CART_TOKEN_COOKIE, + anonymousCartTokenCookie: REACTION_ANONYMOUS_CART_TOKEN_COOKIE, shopId: SHOP_ID, } diff --git a/framework/reactioncommerce/provider.ts b/framework/reactioncommerce/provider.ts index 13b7af197..fac81994a 100644 --- a/framework/reactioncommerce/provider.ts +++ b/framework/reactioncommerce/provider.ts @@ -1,6 +1,6 @@ import { REACTION_ANONYMOUS_CART_TOKEN_COOKIE, - REACTION_CART_ID_COOKIE, + REACTION_ANONYMOUS_CART_ID_COOKIE, STORE_DOMAIN, } from './const' @@ -20,8 +20,8 @@ import fetcher from './fetcher' export const reactionCommerceProvider = { locale: 'en-us', - cartCookie: REACTION_ANONYMOUS_CART_TOKEN_COOKIE, - cartIdCookie: REACTION_CART_ID_COOKIE, + anonymousCartTokenCookie: REACTION_ANONYMOUS_CART_TOKEN_COOKIE, + anonymousCartIdCookie: REACTION_ANONYMOUS_CART_ID_COOKIE, storeDomain: STORE_DOMAIN, fetcher, cart: { useCart, useAddItem, useUpdateItem, useRemoveItem }, diff --git a/framework/reactioncommerce/utils/get-cart-id.ts b/framework/reactioncommerce/utils/get-cart-id.ts index 14a4ce05e..d857fe01e 100644 --- a/framework/reactioncommerce/utils/get-cart-id.ts +++ b/framework/reactioncommerce/utils/get-cart-id.ts @@ -1,8 +1,8 @@ import Cookies from 'js-cookie' -import { REACTION_CART_ID_COOKIE } from '../const' +import { REACTION_ANONYMOUS_CART_ID_COOKIE } from '../const' const getCartId = (id?: string) => { - return id ?? Cookies.get(REACTION_CART_ID_COOKIE) + return id ?? Cookies.get(REACTION_ANONYMOUS_CART_ID_COOKIE) } export default getCartId diff --git a/framework/reactioncommerce/utils/mutations/reconcile-carts.ts b/framework/reactioncommerce/utils/mutations/reconcile-carts.ts new file mode 100644 index 000000000..07d65d7de --- /dev/null +++ b/framework/reactioncommerce/utils/mutations/reconcile-carts.ts @@ -0,0 +1,13 @@ +import { cartPayloadFragment } from '@framework/utils/queries/get-checkout-query' + +const reconcileCartsMutation = ` + mutation reconcileCartsMutation($input: ReconcileCartsInput!) { + reconcileCarts(input: $input) { + cart { + ${cartPayloadFragment} + } + } + } +` + +export default reconcileCartsMutation diff --git a/framework/reactioncommerce/utils/queries/account-cart-by-account-id.ts b/framework/reactioncommerce/utils/queries/account-cart-by-account-id.ts new file mode 100644 index 000000000..3e245b569 --- /dev/null +++ b/framework/reactioncommerce/utils/queries/account-cart-by-account-id.ts @@ -0,0 +1,11 @@ +import { cartQueryFragment } from '@framework/utils/queries/get-checkout-query' + +const accountCartByAccountIdQuery = ` + query accountCartByAccountIdQuery($accountId: ID!, $shopId: ID!, $itemsAfterCursor: ConnectionCursor) { + cart: accountCartByAccountId(accountId: $accountId, shopId: $shopId) { + ${cartQueryFragment} + } + } +` + +export default accountCartByAccountIdQuery diff --git a/framework/reactioncommerce/utils/queries/get-customer-id-query.ts b/framework/reactioncommerce/utils/queries/get-customer-id-query.ts index 51fef14e7..9940dd0e0 100644 --- a/framework/reactioncommerce/utils/queries/get-customer-id-query.ts +++ b/framework/reactioncommerce/utils/queries/get-customer-id-query.ts @@ -1,8 +1,8 @@ -export const viewerQuery = /* GraphQL */ ` - query getCustomerId($customerAccessToken: String!) { - customer(customerAccessToken: $customerAccessToken) { - id +export const viewerIdQuery = /* GraphQL */ ` + query viewer { + viewer { + _id } } ` -export default viewerQuery +export default viewerIdQuery