# Updating to 2024-07 Some checkouts may be created with [cart instructions](/docs/api/checkout-ui-extensions/apis/cart-instructions) that prevent buyers from making certain changes to their checkout. As of version `2024-07`, UI extensions must check for instructions before calling select APIs, to properly handle checkouts where those APIs are not available. > Caution: As of **July 22nd, 2024**, all UI extensions on this version will render in **draft order** invoice checkouts. As draft order invoice checkouts have restrictions on what buyers can edit, UI extensions in draft order invoice checkouts will be subject to cart instructions. ## Required instruction checking ### Required instruction checking You will need to check for cart instructions before calling the following APIs: | Extension API | As of July 2024 | | ---- | ----- | | applyAttributeChange() | Attributes cannot be modified on draft order checkouts. | | applyShippingAddressChange() | Buyers cannot change the address on a draft order checkout if it has fixed shipping rates. | | applyDiscountCodeChange() | By default, discounts cannot be modified in draft order checkouts. Merchants must allow it via a setting on the draft order. | | applyCartLinesChange() | Cart lines cannot be modified on draft order checkouts. | | applyMetafieldChange() | Cart metafields cannot be modified on draft order checkouts. Metafields can still be modified. | | applyNoteChange() | Notes cannot be modified on draft order checkouts. | ## Checking for cart instructions ### Changes to applyAttributeChange() Check `instructions.attributes.canUpdateAttributes` before calling `applyAttributeChange()`. ### Migrating applyAttributeChange() ```jsx import { Banner, Button, useApplyAttributeChange, useInstructions, reactExtension, } from '@shopify/ui-extensions-react/checkout'; export default reactExtension( 'purchase.checkout.block.render', () => <Extension />, ); function Extension() { const applyAttributeChange = useApplyAttributeChange(); const instructions = useInstructions(); if ( instructions.attributes.canUpdateAttributes ) { return ( <Button onPress={() => applyAttributeChange({ type: 'updateAttribute', key: 'loyaltyPoints', value: '100', }) } > Apply 100 loyalty points </Button> ); } else { return ( <Banner status="warning"> Loyalty points are unavailable </Banner> ); } } ``` ```js import { extension, Banner, Button, } from '@shopify/ui-extensions/checkout'; export default extension( 'purchase.checkout.block.render', (root, api) => { if ( api.instructions.current.attributes .canUpdateAttributes ) { root.appendChild( root.createComponent( Button, { onPress: () => api.applyAttributeChange({ type: 'updateAttribute', key: 'loyaltyPoints', value: '100', }), }, 'Apply 100 loyalty points', ), ); } else { root.appendChild( root.createComponent( Banner, {}, 'Loyalty points are unavailable', ), ); } }, ); ``` ### Changes to applyShippingAddressChange() Check `instructions.delivery.canSelectCustomAddress` before calling `applyShippingAddressChange()`. When `true`, this instruction implies that extensions can change the shipping address. ### Migrating applyShippingAddressChange() ```jsx import { Banner, Button, useApplyShippingAddressChange, useInstructions, reactExtension, } from '@shopify/ui-extensions-react/checkout'; export default reactExtension( 'purchase.checkout.block.render', () => <Extension />, ); function Extension() { const applyShippingAddressChange = useApplyShippingAddressChange(); const instructions = useInstructions(); if ( instructions.delivery.canSelectCustomAddress ) { return ( <Button onPress={() => applyShippingAddressChange({ type: 'updateShippingAddress', address: { zip: '90201', }, }) } > Change your postal code </Button> ); } else { return ( <Banner status="warning"> Shipping address cannot be modified </Banner> ); } } ``` ```js import { extension, Banner, Button, } from '@shopify/ui-extensions/checkout'; export default extension( 'purchase.checkout.block.render', (root, api) => { if ( api.instructions.current.delivery .canSelectCustomAddress ) { root.appendChild( root.createComponent( Button, { onPress: () => api.applyShippingAddressChange({ type: 'updateShippingAddress', address: { zip: '90201', }, }), }, 'Change your postal code', ), ); } else { root.appendChild( root.createComponent( Banner, {}, 'Shipping address cannot be modified', ), ); } }, ); ``` ### Changes to applyDiscountCodeChange() Check `instructions.discounts.canUpdateDiscountCodes` before calling `applyDiscountCodeChange()`. ### Migrating applyDiscountCodeChange() ```jsx import { Banner, Button, useApplyDiscountCodeChange, useInstructions, reactExtension, } from '@shopify/ui-extensions-react/checkout'; export default reactExtension( 'purchase.checkout.block.render', () => <Extension />, ); function Extension() { const applyDiscountCodeChange = useApplyDiscountCodeChange(); const instructions = useInstructions(); if ( instructions.discounts.canUpdateDiscountCodes ) { return ( <Button onPress={() => applyDiscountCodeChange({ type: 'addDiscountCode', code: 'FREE_SHIPPING', }) } > Apply your loyalty discount </Button> ); } else { return ( <Banner status="warning"> Loyalty discounts are unavailable </Banner> ); } } ``` ```js import { extension, Banner, Button, } from '@shopify/ui-extensions/checkout'; export default extension( 'purchase.checkout.block.render', (root, api) => { if ( api.instructions.current.discounts .canUpdateDiscountCodes ) { root.appendChild( root.createComponent( Button, { onPress: () => api.applyDiscountCodeChange({ type: 'addDiscountCode', code: 'FREE_SHIPPING', }), }, 'Apply your loyalty discount', ), ); } else { root.appendChild( root.createComponent( Banner, {}, 'Loyalty discounts are unavailable', ), ); } }, ); ``` ### Changes to applyCartLinesChange() Check `instructions.lines.canAddCartLine` or `instructions.lines.canRemoveCartLine` or `instructions.lines.canUpdateCartLine` before calling `applyCartLinesChange()`. ### Migrating applyCartLinesChange() ```jsx import { Banner, Button, useApplyCartLinesChange, useInstructions, reactExtension, } from '@shopify/ui-extensions-react/checkout'; export default reactExtension( 'purchase.checkout.block.render', () => <Extension />, ); function Extension() { const applyCartLinesChange = useApplyCartLinesChange(); const instructions = useInstructions(); if (instructions.lines.canAddCartLine) { return ( <Button onPress={() => applyCartLinesChange({ type: 'addCartLine', merchandiseId: 'gid://shopify/product/1234', quantity: 1, }) } > Add a free gift to your order </Button> ); } else { return ( <Banner status="warning"> The products in your cart cannot be modified </Banner> ); } } ``` ```js import { extension, Banner, Button, } from '@shopify/ui-extensions/checkout'; export default extension( 'purchase.checkout.block.render', (root, api) => { if ( api.instructions.current.lines .canAddCartLine ) { root.appendChild( root.createComponent( Button, { onPress: () => api.applyCartLinesChange({ type: 'addCartLine', merchandiseId: 'gid://shopify/product/1234', quantity: 1, }), }, 'Add a free gift to your order', ), ); } else { root.appendChild( root.createComponent( Banner, {}, 'The products in your cart cannot be modified', ), ); } }, ); ``` ### Changes to applyMetafieldChange() Check `instructions.metafields.canSetCartMetafields` or `instructions.metafields.canDeleteCartMetafields` before calling `applyMetafieldChange()` if you are working with cart metafields. ### Migrating applyMetafieldChange() ```jsx import { Banner, Button, useApplyMetafieldChange, useInstructions, reactExtension, } from '@shopify/ui-extensions-react/checkout'; export default reactExtension( 'purchase.checkout.block.render', () => <Extension />, ); function Extension() { const applyMetafieldChange = useApplyMetafieldChange(); const instructions = useInstructions(); if ( instructions.metafields.canSetCartMetafields ) { return ( <Button onPress={() => applyMetafieldChange({ type: 'updateCartMetafield', metafield: { namespace: 'loyalty', key: 'loyaltyPoints', value: '100', type: 'string', }, }) } > Apply 100 loyalty points </Button> ); } else { return ( <Banner status="warning"> Loyalty points are unavailable </Banner> ); } } ``` ```js import { extension, Banner, Button, } from '@shopify/ui-extensions/checkout'; export default extension( 'purchase.checkout.block.render', (root, api) => { if ( api.instructions.current.metafields .canSetCartMetafields ) { root.appendChild( root.createComponent( Button, { onPress: () => api.applyMetafieldChange({ type: 'updateCartMetafield', metafield: { namespace: 'loyalty', key: 'loyaltyPoints', value: '100', type: 'string', }, }), }, 'Apply 100 loyalty points', ), ); } else { root.appendChild( root.createComponent( Banner, {}, 'Loyalty points are unavailable', ), ); } }, ); ``` ### Changes to applyNoteChange() Check `instructions.notes.canUpdateNote` before calling `applyNoteChange()`. ### Migrating applyNoteChange() ```jsx import { Banner, Button, useApplyNoteChange, useInstructions, reactExtension, } from '@shopify/ui-extensions-react/checkout'; export default reactExtension( 'purchase.checkout.block.render', () => <Extension />, ); function Extension() { const applyNoteChange = useApplyNoteChange(); const instructions = useInstructions(); if (instructions.notes.canUpdateNote) { return ( <Button onPress={() => applyNoteChange({ type: 'updateNote', note: 'Please include a free gift.', }) } > Include a free gift with your order </Button> ); } else { return ( <Banner status="warning"> Free gifts cannot be added to this order </Banner> ); } } ``` ```js import { extension, Banner, Button, } from '@shopify/ui-extensions/checkout'; export default extension( 'purchase.checkout.block.render', (root, api) => { if ( api.instructions.current.notes.canUpdateNote ) { root.appendChild( root.createComponent( Button, { onPress: () => api.applyNoteChange({ type: 'updateNote', note: 'Please include a free gift.', }), }, 'Include a free gift with your order', ), ); } else { root.appendChild( root.createComponent( Banner, {}, 'Free gifts cannot be added to this order', ), ); } }, ); ``` ### Checking for cart instructions Use the cart instructions API to determine if the affected APIs are available in checkout.