Open lynthius opened 6 months ago
Hey, any chance to put this issue on the radar in the near future?
Seeing metafield update issues in Checkout UI Extensions too without any response yet.
Hopefully it's just a large backlog, or a refactor coming that improves metafield support for all APIs.
@lynthius I wasn't able reproduce this issue. Is the customer logged into checkout and have the appropriate metafield value set already?
@lynthius I wasn't able reproduce this issue. Is the customer logged into checkout and have the appropriate metafield value set already?
@jamesvidler yes, you have to create a metafield for the user and pass there some value. Then try to read that value :) I can't do this. I'm not able to read any customer's metafield. I'm logged into checkout.
EDITED: 13/05/24
Also, I've noticed the useCustomer
hook doesn't return the customer data either on the customer-account.order-status.block.render
extension point.
The useOrder
hook works just fine, and it returns the customer data:
const order = useOrder();
const customer = useCustomer();
const email = useEmail()
<>
<View>{JSON.stringify(order, null, 4)}</View>
<View>{JSON.stringify(customer, null, 4) || "no customer data"}</View>
<View>{email}</View>
</>
Results in:
Actually, my comment is probably more closely aligned with this other bug report: https://github.com/Shopify/ui-extensions/issues/1614
Hello again, please recheck this issue. Still, I don't know how to resolve it, and is it even possible?
Hello there, anyone found solution on this?
@Vinitpal nope, still waiting. I hope we will find a solution.
@lynthius I reviewed the code you provided and attempted to reproduce again. I have some ideas of what you might be running into:
appMetafields
are loaded asynchronously, after checkout loads which means that the initial value of appMetafields
may be empty. However, since you are using useAppMetafields()
, your extension will re-render once it has a value. Your code, as is isn't checking if referralCode[0]
has a value before accessing it so the extension is crashing before it has a chance to re-render. I took your exact code example and reproduced this. I removed the reference for referralCode[0].metafield
and I can see the metafield values returned now in the console.log
.Let me know any this helps!
@jamesvidler but I test it in the development environment with my sandbox store. There is no way to add "protected customer data" access, at least I don't see it. What scope should I add to the toml file?
I will share with you my new implementation and please tell me if something is wrong with it. It's a simple VAT field. It should add VAT metafield with value for ORDER and CUSTOMER objects. It works for ORDER, but again not with CUSTOMER.
EDIT: I can read the customer's metafield with VAT now if it's not empty. But still, I can't overwrite it or add value to the empty one.
EDIT2: Is it possible I couldn't read the customer's metafield value earlier because I tried to do it on the thank you status page, and I migrated only partly on the new checkout extensibility (without thank you, orders status pages)? If yes, then it is a little bit tricky because I can migrate fully until August 2025, but on the other hand, I can't use all of the functionalities.
import {
reactExtension,
BlockStack,
TextField,
useApplyMetafieldsChange,
useAppMetafields,
Checkbox,
Heading,
useSettings,
useExtensionCapability,
useBuyerJourneyIntercept,
useApi,
useTranslate,
} from "@shopify/ui-extensions-react/checkout";
import { useState } from "react";
// export default reactExtension("purchase.checkout.block.render", () => <Extension />);
export default reactExtension("purchase.checkout.contact.render-after", () => <Extension />);
function Extension() {
const [checked, setChecked] = useState(false);
const metafieldNamespaceCheckout = "checkout";
const metafieldNamespaceCustomer = "customer";
const metafieldKey = "vat_number";
const translate = useTranslate();
const invoiceFieldTranslation = translate("invoice_field_heading");
const invoiceCheckboxTranslation = translate("invoice_checkbox_info");
const invoicePlaceholderTranslation = translate("invoice_placeholder_info");
const invoicePlaceholderOptionalTranslation = translate("invoice_placeholder_optional_info");
const invoiceVatRequiredTranslation = translate("invoice_vat_required_info");
const invoiceVatNotCorrectTranslation = translate("invoice_vat_not_correct_info");
const invoiceProvideVatTranslation = translate("invoice_provide_vat_info");
const invoiceFixVatTranslation = translate("invoice_fix_vat_not_info");
const settings = useSettings();
const visibilityFlag = settings.vat_visibility as boolean;
const applyMetafieldsChange = useApplyMetafieldsChange();
const whatMetafields = useAppMetafields();
const handleChangeCheckbox = () => {
console.log("metafields list:", whatMetafields);
setChecked(!checked);
};
const [vat, setVat] = useState("");
const [validationError, setValidationError] = useState("");
const canBlockProgress = useExtensionCapability("block_progress");
const label = canBlockProgress ? invoicePlaceholderTranslation : invoicePlaceholderOptionalTranslation;
useBuyerJourneyIntercept(({ canBlockProgress }) => {
if (canBlockProgress && checked && !isVatSet()) {
return {
behavior: "block",
reason: invoiceVatRequiredTranslation,
perform: (result) => {
if (result.behavior === "block") {
setValidationError(invoiceProvideVatTranslation);
}
},
};
}
if (canBlockProgress && checked && !isVatValid(vat)) {
return {
behavior: "block",
reason: invoiceVatNotCorrectTranslation,
perform: (result) => {
if (result.behavior === "block") {
setValidationError(invoiceFixVatTranslation);
}
},
// errors: [
// {
// message: "The VAT number entered is incorrect. Please ensure it starts with a country code (uppercase), followed by 8 to 12 alphanumeric characters. Example: XX0123456789.",
// },
// ],
};
}
return {
behavior: "allow",
perform: () => {
clearValidationErrors();
},
};
});
const handleChangeVatInput = (value: string) => {
setVat(value);
if (isVatValid(value)) {
applyMetafieldsChange({
type: "updateMetafield",
namespace: metafieldNamespaceCheckout,
key: metafieldKey,
valueType: "string",
value,
});
applyMetafieldsChange({
type: "updateMetafield",
namespace: metafieldNamespaceCustomer,
key: metafieldKey,
valueType: "string",
value,
});
}
};
function isVatSet() {
return vat !== "";
}
function isVatValid(value: string): boolean {
const vatTarget: RegExp = /^[A-Z]{2}[0-9A-Z]{8,12}$/; // Example regex for VAT number validation
return vatTarget.test(value);
}
function clearValidationErrors() {
setValidationError("");
}
if (!visibilityFlag) return null;
return (
<>
<BlockStack padding={["base", "none", "base", "none"]}>
<Heading level={2}>{invoiceFieldTranslation}</Heading>
</BlockStack>
<BlockStack>
<BlockStack border="base" borderRadius="base" padding="base">
<Checkbox checked={checked} onChange={handleChangeCheckbox}>
{invoiceCheckboxTranslation}
</Checkbox>
</BlockStack>
{checked && (
<TextField
label={label}
value={vat}
onInput={clearValidationErrors}
onChange={(value) => handleChangeVatInput(value)}
required={canBlockProgress && checked}
error={validationError}
/>
)}
</BlockStack>
</>
);
}
[access_scopes]
# Learn more at https://shopify.dev/docs/apps/tools/cli/configuration#access_scopes
scopes = "customer_read_customers,customer_read_draft_orders,customer_read_markets,customer_read_orders,customer_write_customers,read_cart_transforms,read_checkouts,read_content,read_customers,write_content,write_customers"
# Learn more about configuring your checkout UI extension:
# https://shopify.dev/api/checkout-extensions/checkout/configuration
# The version of APIs your extension will receive. Learn more:
# https://shopify.dev/docs/api/usage/versioning
api_version = "2024-04"
[[extensions]]
type = "ui_extension"
name = "checkout-ui"
handle = "checkout-ui"
# Controls where in Shopify your extension will be injected,
# and the file that contains your extension’s source code. Learn more:
# https://shopify.dev/docs/api/checkout-ui-extensions/unstable/extension-targets-overview
[[extensions.targeting]]
module = "./src/Checkout.tsx"
target = "purchase.checkout.block.render"
[extensions.capabilities]
# Gives your extension access to directly query Shopify’s storefront API.
# https://shopify.dev/docs/api/checkout-ui-extensions/unstable/configuration#api-access
api_access = true
block_progress = true
# Gives your extension access to make external network calls, using the
# JavaScript `fetch()` API. Learn more:
# https://shopify.dev/docs/api/checkout-ui-extensions/unstable/configuration#network-access
network_access = true
# Loads metafields on checkout resources, including the cart,
# products, customers, and more. Learn more:
# https://shopify.dev/docs/api/checkout-ui-extensions/unstable/configuration#metafields
[[extensions.metafields]]
namespace = "checkout"
key = "vat_number"
[[extensions.metafields]]
namespace = "customer"
key = "vat_number"
# [[extensions.metafields]]
# namespace = "my_namespace"
# key = "my_other_key"
# Defines settings that will be collected from merchants installing
# your extension. Learn more:
# https://shopify.dev/docs/api/checkout-ui-extensions/unstable/configuration#settings-definition
[extensions.settings]
[[extensions.settings.fields]]
key = "vat_visibility"
type = "boolean"
name = "VAT visibility"
description = "Show or hide VAT number filed"
Fun fact, I use the Flow app to fix my problem (adding the same VAT value for the customer's metafield from input inside checkout by copying it from the last order's VAT metafield. But it would be great to send it from the checkout using applyMetafieldsChange.
@lynthius, I am facing the exact same issue. Every other metafield is being returned except the customer related ones. I have one test app that I created a while back and that one is returning it. I have compared the toml files of both and the apps are exactly the same, they both have the same scope permission as well.
I compared it with yours and it seems to have the same logic. I am not sure what is going on. I know that you shared your updates, but any additional information would help. I am a little stuck with this issue. Thank you in advance!
@rodrigo-jantsch have you fully migrated to the Checkout UI extensions? If you want to grab the customer's metafields from the Thank you page, this could be the problem. I finally returned the customer's metafield value, but for the Checkout page, not the Thank you page. Still, I can't overwrite it or add value to the empty one, and I'm not sure why. I have all the permissions. Could you help us @jamesvidler?
@lynthius , no, we are migrating the entire store, so we are starting from the scratch using the Checkout Uo extensions. I am actually trying to get the customer data from the Checkout Page once the customer is logged in....
Apparently you have to deploy it when there are scope changes. It was that!
Please list the package(s) involved in the issue, and include the version you are using
Describe the bug
Customer metafield should be available in the Shopify Checkout for the Order Status Page but they are not. It's not possible to retrieve the value of customer-type metafields within the checkout. It seems to only work with other metafields types like product metafields.
Steps to reproduce the behavior:
[[extensions.metafields]] namespace = "custom" key = "referral_code"
[[extensions.metafields]] namespace = "test_data" key = "snowboard_length"
Checkout.tsx
import { reactExtension, TextBlock, BlockStack, useAppMetafields, } from "@shopify/ui-extensions-react/checkout";
export default reactExtension("purchase.checkout.payment-method-list.render-after", () => );
function Extension() { const referralCode = useAppMetafields({ type: "customer", namespace: "custom", key: "referral_code", });
const appMetafields = useAppMetafields(); console.log(appMetafields); console.log(referralCode);
return ( <>
); }