B2B Integration
Follow these steps to integrate Shopify's B2B functionality. If the repo was created before Blueprint version 1.14.0, then the New Customer Account API migration is required before continuing.
Code Migration
The commit links (paired with some steps) direct to a Blueprint PR used only for reference purposes.
Return buyer from root loader
Update the root.tsx loader to fetch and return buyer [commit]
Storefront API buyer query variable for Graphql
Add buyer variable to every Storefront API Graphql query for only product, collection, and search. Do not add to queries for the Pack API [example]
@inContext(country: $country, language: $language) to see where Storefront API queries exist. For each query:
- Add
$buyer: BuyerInputto the variables - Add
buyer: $buyerinto@inContext()
For example, this product query:
export const PRODUCT_QUERY = `#graphql
query Product(
$handle: String!
$country: CountryCode
$language: LanguageCode
) @inContext(country: $country, language: $language) {
...
}
`;
Becomes:
export const PRODUCT_QUERY = `#graphql
query Product(
$handle: String!
$country: CountryCode
$language: LanguageCode
$buyer: BuyerInput
) @inContext(country: $country, language: $language, buyer: $buyer) {
...
}
`;
Storefront API buyer query variable for client
Add buyer variable to every storefront client query for product, collection, and search. This does not apply to the pack or admin clients.
- In
app/libcreate a new file calledb2b.server.ts[commit]
import type {AppLoadContext} from '@shopify/remix-oxygen';
/* Buyer contextualization for B2B */
export const getBuyerVariables = async (context: AppLoadContext) => {
const {customerAccount} = context;
const buyer = await customerAccount.getBuyer();
return buyer?.companyLocationId && buyer?.customerAccessToken
? {
buyer: {
companyLocationId: buyer.companyLocationId,
customerAccessToken: buyer.customerAccessToken,
},
}
: null;
};
-
Search the codebase for every instance of
storefront.query, which will occur inloaderor server logic -
If the storefront query is related to product, collection or search, the
buyervariable needs to be passed in [example]
- In Blueprint, the routes that had this change were:
api.collection,api.predictive-search,api.product-by-id,api.product,api.recommendations,api.search,collections.$handle,products.$handleandsearch - And files
products.server.tsandserver.utils.ts,
Example:
import {getBuyerVariables} from '~/lib/b2b.server';
...
export async function loader({context, request}: LoaderFunctionArgs) {
...
const buyerVariables = await getBuyerVariables(context);
let {product} = await storefront.query(PRODUCT_QUERY, {
variables: {
handle,
selectedOptions,
country: storefront.i18n.country,
language: storefront.i18n.language,
...buyerVariables,
},
cache: storefront.CacheShort(),
});
server.utils.ts, getBuyerVariables cannot be imported because of a React Refresh rule. Instead just fetch and generate the buyer variable in the same code block [commit] - There is also a change to the props for
getFiltersinserver.utils.ts[commit], which gives way to a change incollections.$handle[commit] andsearch[commit]
B2B Provider and Components
- Add route
($locale).api.b2blocations.tsxto theapp/routesfolder [commit] - Download this
B2Bfolder and paste folder into theapp/componentsfolder - Add
<B2BLocationProvider>alongside where all otherProvider's wrap the html [commit]
- The provider also will automatically open
B2BLocationSelectorModalif the customer has not yet selected a location. The modal will only close once a location is selected. Changes toModalare made to accommodate this new logic (instructions in later steps) - Depending on the Blueprint version,
openModalinB2BLocationProviderandcloseModalinB2BLocationSelectorModalwill come from eitheruseMenuoruseGlobal. If it's fromuseGlobal, correct accordingly
- Add
<B2BQuantityRules />and<B2BPriceBreaks />under the PDPAddToCart[commit]
- These display the quantity rules and price breaks, respectively for the selected variant
- Add
<B2BLocation />to the customer account menu [commit]
- This displays the customer's location and a button to open the
B2BLocationSelectorModalto switch location openModalfromuseMenuwill also need to be corrected if comes fromuseGlobal
Add quantityRule and quantityPriceBreaks fields
- Add
quantityRuleandquantityPriceBreakstoVARIANT_FRAGMENT[commit] - Add
quantityRuleandquantityPriceBreaksinsidemerchandiseinCART_LINE_FRAGMENTandCART_LINE_COMPONENT_FRAGMENT[commit]
Add quantityRule logic for quantity and quantity selectors
Apply the increment, minimum and maximum settings to all quantity and quantity selectors
- Change default PDP
quantityfrom1to theminimumvalue [commit] - Add
quantityRulelogic for the PDPQuantitySelector[commit] - Add
quantityRulelogic for theuseCartLinehook [commit] - Pass
disableDecrementanddisableIncrementto the cart lineQuantitySelector[commit] - Wherever else there is a default product
quantity, e.g. quick shop, apply the same change from step 1 - Wherever else
QuantitySelectoris used, apply the same logic from step 2 or 3
Customer types
- Add
customer.types.tsto theapp/lib/typesfolder [commit] - Add its export to the
index.tsfile [commit]
Add disableClose prop to Modal
This additional Modal setting is to disable the customer from closing the modal when asked to choose a location, if a location has not yet been selected
- In either
MenuProvider.tsxorGlobalProvider.tsx(depending on what version Blueprint the store was built on), updatedefaultModalandopenModal[commit] - In either
context.types.tsorglobal.types.ts, adddisableClosetype toModaland updateopenModaltype [commit] - In
Modal.tsx, update theonCloseattribute forDialogand wrap the close button in adisableCloseconditional [commit]
Update buyerIdentity
Add purchasingCompany field inside buyerIdentity in CART_FRAGMENT [commit]