invoice view button
The invoice view button extension point adds custom buttons to the invoice detail page in the Adobe Commerce Admin. Declare buttons under adminUi.invoice.viewButtons in app.commerce.config.ts. Use type: "view" to open an app page in an iframe, or type: "worker" to invoke a runtime action without displaying an iframe.
Example customization
The following configuration adds an Invoice details button that opens an iframe and a Send invoice button that invokes a runtime action after confirmation:
adminUi: {
invoice: {
viewButtons: [
{
id: "invoice-details",
label: "Invoice details",
type: "view",
path: "#/invoice-details",
title: "Custom invoice details",
level: 0,
sortOrder: 100,
sandboxPermissions: ["allow-modals"],
},
{
id: "send-invoice",
label: "Send invoice",
type: "worker",
runtimeAction: "invoice/send",
timeout: 15,
sortOrder: 110,
confirm: { message: "Send this invoice?" },
notifications: {
success: "Invoice sent successfully.",
error: "Unable to send the invoice.",
},
},
],
},
}
V2 registration is served through the generated app-config action. The SDK generates the required web and worker operation declarations and includes invoice buttons in installation and upgrade planning. Implement the frontend route or runtime handler referenced by each button. See Build and deploy.
How it works
- A merchant opens an invoice in Commerce Admin.
- Commerce renders the buttons declared in
invoice.viewButtonsalongside the built-in buttons. - If configured, Commerce shows a confirmation dialog before running the action.
- A
viewbutton opens the configuredpathin an iframe. The page reads the invoice ID withuseInvoiceViewButtonContext()and closes withuseHostConnection(). - A
workerbutton invokes the configuredruntimeActionwith the invoice ID. No iframe is shown. - Commerce uses the configured success or error notifications when the action completes.
Parameters
idinvoice.viewButtons after Commerce ID sanitization.labeldescriptiontypeview (opens an iframe) or worker (invokes a runtime action).pathtype: "view"#/invoice-details.titleview onlyworker buttons.sandboxPermissionsview onlyallow-downloads, allow-modals, and allow-popups.runtimeActiontype: "worker"<package>/<action> format from the app's runtime manifest. The SDK generates its workerProcess declaration.timeoutworker onlyconfirm.titleconfirm.messagelevel-1 (left), 0 (center), or 1 (right).sortOrdernotifications.successnotifications.erroraclProtectedtrue, gates the button behind a dedicated Commerce ACL resource scoped to the app. The default is false.The variants are strict. Setting runtimeAction or timeout on a view button, or path, title, or sandboxPermissions on a worker button, fails configuration validation.
Read the iframe context
useInvoiceViewButtonContext() returns { data, error }, with data.invoiceId as a string on success. It reads invoiceId from the page URL's search parameters or hash-route query, not from the Commerce shared context. A missing or blank invoice ID returns an error.
import {
useHostConnection,
useInvoiceViewButtonContext,
} from "@adobe/aio-commerce-lib-admin-ui/web";
export function InvoiceDetailsPage() {
const { data, error } = useInvoiceViewButtonContext();
const { actions, error: hostError } = useHostConnection();
if (error || hostError) {
return <p role="alert">{(error || hostError)?.message}</p>;
}
return (
<div>
<p>Invoice: {data.invoiceId}</p>
<button onClick={() => actions.close()}>Done</button>
</div>
);
}
Call actions.close() to complete the iframe flow and return to the originating invoice, or actions.closeWithError() to return with an error indication.
Implement a worker handler
Use the helpers exported by @adobe/aio-commerce-lib-admin-ui/invoice-view-buttons to parse requests and build responses:
import {
okInvoiceViewButtonResponse,
parseInvoiceViewButtonRequest,
} from "@adobe/aio-commerce-lib-admin-ui/invoice-view-buttons";
import { sendInvoice } from "./invoice-service.js";
export async function main(params: unknown) {
const { invoiceId } = parseInvoiceViewButtonRequest(params);
await sendInvoice(invoiceId);
return okInvoiceViewButtonResponse();
}
Implement sendInvoice in your app's invoice-service.js module to perform the business operation.
The parsed request contains three required, non-empty strings:
requestIdidinvoiceIdorderId cannot substitute for this field.parseInvoiceViewButtonRequest strips extra runtime parameters and throws CommerceSdkValidationError for malformed requests. okInvoiceViewButtonResponse() returns HTTP 200 with an empty {} body. To report a failure, return invoiceViewButtonErrorResponse(statusCode, message), which preserves the supplied HTTP status and returns { message } in the body.
ACL protection
Set aclProtected: true to generate a dedicated resource for the invoice button. Commerce administrators can grant or restrict that resource per role. See Access control list protection.
In a protected worker handler, check permissions with getAdminUiPermissionClient before performing the operation. Derive the resource ID with getInvoiceViewButtonAclResourceId(metadataId, buttonId) from @adobe/aio-commerce-lib-admin-ui/invoice-view-buttons. Use the clicked button's id, not invoiceId. See ACL-protected extension points for the permission-check pattern.