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: BuyerInput
to the variables - Add
buyer: $buyer
into@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/lib
create 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 inloader
or server logic -
If the storefront query is related to product, collection or search, the
buyer
variable 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.$handle
andsearch
- And files
products.server.ts
andserver.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
getFilters
inserver.utils.ts
[commit], which gives way to a change incollections.$handle
[commit] andsearch
[commit]
B2B Provider and Components
- Add route
($locale).api.b2blocations.tsx
to theapp/routes
folder [commit] - Download this
B2B
folder and paste folder into theapp/components
folder - Add
<B2BLocationProvider>
alongside where all otherProvider
's wrap the html [commit]
- The provider also will automatically open
B2BLocationSelectorModal
if the customer has not yet selected a location. The modal will only close once a location is selected. Changes toModal
are made to accommodate this new logic (instructions in later steps) - Depending on the Blueprint version,
openModal
inB2BLocationProvider
andcloseModal
inB2BLocationSelectorModal
will come from eitheruseMenu
oruseGlobal
. 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
B2BLocationSelectorModal
to switch location openModal
fromuseMenu
will also need to be corrected if comes fromuseGlobal
Add quantityRule
and quantityPriceBreaks
fields
- Add
quantityRule
andquantityPriceBreaks
toVARIANT_FRAGMENT
[commit] - Add
quantityRule
andquantityPriceBreaks
insidemerchandise
inCART_LINE_FRAGMENT
andCART_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
quantity
from1
to theincrement
value [commit] - Add
quantityRule
logic for the PDPQuantitySelector
[commit] - Add
quantityRule
logic for theuseCartLine
hook [commit] - Pass
disableDecrement
anddisableIncrement
to 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
QuantitySelector
is used, apply the same logic from step 2 or 3
Customer types
- Add
customer.types.ts
to theapp/lib/types
folder [commit] - Add its export to the
index.ts
file [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.tsx
orGlobalProvider.tsx
(depending on what version Blueprint the store was built on), updatedefaultModal
andopenModal
[commit] - In either
context.types.ts
orglobal.types.ts
, adddisableClose
type toModal
and updateopenModal
type [commit] - In
Modal.tsx
, update theonClose
attribute forDialog
and wrap the close button in adisableClose
conditional [commit]
Update buyerIdentity
Add purchasingCompany
field inside buyerIdentity
in CART_FRAGMENT
[commit]