Storefront E-Invoice Request
Watch these as presentations — 7 on this page, slides with narration.
Let a shopper who paid at a branch or on your web store claim their own e-invoice from the storefront, with no account needed. The sale leaves your monthly consolidated e-invoice and goes to LHDN in the shopper’s name, with no approval step and no notice to you: the Portal Request list in My E-Invoice Admin is where you see it, and a failed row there needs somebody.
Overview
A shopper who paid at a branch or on the web store can ask for their own Malaysian LHDN e-invoice from the storefront itself — no account required. They type the cash-bill number with either the PIN printed on the receipt or the amount and date, confirm it is their transaction, fill in their identification number and tax identification number (TIN), and the sale is taken out of the merchant’s monthly consolidated e-invoice and submitted to LHDN as an individual one in their name.
That path is a widget in the Cross Platform Commerce storefront app (the web and mobile Customer Portal), not a screen in an applet. It is placed on a Customer Portal page from CP Commerce Admin, and everything it does runs through a handful of server calls and one queued request run.
This page documents the storefront path only. The merchant’s side of the same pipeline — the pools, the submission and validation queues, the cancellation requests — is the My E-Invoice Admin applet. The equivalent request flow for a tenant-side buyer account, in a different applet and a different repository, is the MY E-Invoice Portal applet.
Where it fits
| Position | Applet / system | Why |
|---|---|---|
| Module | E-Commerce, E-Invoice | A storefront feature that writes into the e-invoice pipeline. |
| Placed from | CP Commerce Admin | Website → Layout Instance → Nodes → Widget ID → EINVOICE WIDGET. |
| Runs in | The Customer Portal web site and mobile app | The widget itself. |
| Upstream | POS General, Shopping Cart | The cash bill or sales invoice the shopper is asking about, and the pool row it produced. |
| Downstream | My E-Invoice Admin | The request lands as a pool update and a submission-queue row that the operator sees there. |
| Buyer records | Customer | A signed-in shopper’s identification, TIN and e-invoice address are written back to their customer entity; a guest’s are not. |
| Sibling | MY E-Invoice Portal | The same request, made from a tenant-side buyer account instead of a storefront. |
Screens and menus
There is no menu. The widget renders wherever the layout puts it, usually on a page whose code is E-Invoice — the tracking e-mail BigLedger sends links to https://<website code>/page/E-Invoice.
The widget draws a four-item left rail on desktop and a four-item tab strip on mobile. Three of the four are hidden unless the shopper is signed in. Below 550 px, and between 768 px and 1120 px, the rail labels shorten — Request E-Invoice becomes Request, E-Invoice Cancellation becomes Cancellation, and so on.
Request E-Invoice (the search and the request) is open to everyone. Requested E-Invoices is shown to signed-in shoppers, and to a guest only when the page address carries the key and email of the tracking link BigLedger e-mailed them — a guest who closes that e-mail has no other way back to their requests. E-Invoice Cancellation and E-Invoice History are for signed-in shoppers only, so a guest cannot ask to cancel an e-invoice from the storefront at all.
A Help Centre row sits at the bottom of the rail. It is a label with no click handler and no destination.
Request E-Invoice
Two search modes, as sub-tabs:
- By Date Amount (the default) — Cash Bill/Invoice No., Amount, Transaction Date.
- By Pin — Cash Bill/Invoice No., Pin. Hidden when the widget’s
hideByPinparameter is true.
The transaction-date picker does not accept arbitrary dates; see The request window below.
The result is one card (grid view) or one row (list view), showing invoice number, transaction date, currency and amount. Which number is shown as “invoice number” is controlled by the widget’s einvoiceNoDisplayConfig parameter.
Three outcomes:
| What came back | What the shopper sees |
|---|---|
| A pool row that has not been submitted | The result card with a Request button. |
| A record that is already submitted, or has no pool row left | An amber banner — “This invoice has already been submitted. No further requests can be made.” — above a read-only summary of Request Status, Submission Status, Validation Status, Submission Date and Validation Date. No Request button. |
| Nothing matching | A red box with the API’s message, or “Search failed. Please try again.” |
When the widget’s exportPdfPublicAccess parameter is true and the record’s validation status is Valid, an Export button appears on this screen — for anyone who ran the search, signed in or not.
The request pop-up — Update E-Invoice Profile
Pressing Request opens a modal titled Update E-Invoice Profile. It opens for every guest, and for a signed-in shopper whose customer record is missing any of: TIN, identification number, identification type, e-invoice identification type, e-invoice identification value, e-mail, phone, or a billing/shipping address flagged as the default e-invoice address with address line 1, city, state, country and postal code all filled. A signed-in shopper with all of those already on file skips the modal and the request is sent straight away.
After the request
| Path | What the shopper sees |
|---|---|
| Signed in | A modal — a tick, “Submission Successful”, “Thank you requesting for e-invoice. Your request will be processed.”, with Request More and Check Status buttons. |
| Guest | A page message — “Your request will be processed.” |
| Either, on failure | “Request already made by a user.” |
That last message is shown for every failure of the request call, not only for a duplicate — an authorisation failure or a network error produces the same words.
Requested E-Invoices, Cancellation and History
All three are the same list component with different filters, each with a grid/list toggle, a search box (Search Invoice No/Cash Bill No), an After/Before date range and a status drop-down.
- Requested E-Invoices — counters for All Invoices, In Queue, Validated, Validation Error.
- E-Invoice Cancellation — All Invoices, Requested for Rejection, Cancelled.
- E-Invoice History — All Invoices, In Queue, Validated, Validation Error, Requested for Rejection, Cancelled.
Each counter is a separate query, and what each one reads decides what the shopper should do about it:
| Counter | What it counts | What it means |
|---|---|---|
| In Queue | E-invoices whose document status is In Queue or Submitted, plus failed requests on a receipt in the batch or single-general pool | Either LHDN has not seen it yet, or it has it and has not answered — or BigLedger tried and could not build it. All three sit under one number. A failed request on a receipt in the individual pool is not counted anywhere on this screen. |
| Validated | Document status Valid | LHDN accepted it. |
| Validation Error | Document status Invalid — nothing else | LHDN examined the e-invoice and refused it on content. A request that BigLedger never sent is not counted here. |
| Cancelled / Requested for Rejection | Document status Cancelled; the rejection request’s own status | As named. |
So Validation Error always means the document reached LHDN and came back refused — which is the case the shopper cannot re-request, because the request behind it already went through (see Troubleshooting). A request that failed on BigLedger’s own side is folded into In Queue.
The guest version of Requested E-Invoices is a separate, simpler component: no counters, no filters, driven entirely by the key and email in the URL.
The details pop-up
The magnifier icon — always present on a grid card, and on a list row only in the requested, cancellation and history lists — opens a pop-up with invoice number, transaction date, amount and status. When the status is Valid it also renders the LHDN validation QR code, as a base64 PNG held on the record, and an Export button that downloads the rendered e-invoice as E-invoice-<company>-<document type>-<date>-<invoice no>.pdf. On an iOS or Android user-agent the file opens in a new tab instead of downloading, because mobile browsers do not honour the download attribute on blob URLs.
Cancellation
On a validated e-invoice, and only while a countdown of 72 hours from the validation date-time has not run out, a Request for Rejection button appears with the remaining time beside it. It opens a radio list of reasons — Wrong buyer details, Wrong invoice details, Others by default, overridable per widget — with a free-text box when Others is chosen. Once raised, the button is replaced by Requested for Rejection in red.
The countdown is drawn by the widget. Whether the backend independently refuses a late cancellation from this path is not established here.
Configuration
Before you can use it
| Prerequisite | Where | Why |
|---|---|---|
| The hostname carries a CP Commerce public key | Host Name record for the storefront’s domain, extension parameter SYS_AKN_WEB_CP_COMMERCE_PUBLIC_KEY | Without it the storefront cannot authenticate any call and every step of this flow fails with an authorisation error — see below. |
| The hostname carries a default tenant and a website GUID | Same record, SYS_DEFAULT_TENANT and SYS_AKN_WEB_CP_COMMERCE_CMS_WEBSITE_HDR_GUID | The tenant code goes on every call; the website GUID identifies the site on the guest request and guest tracking calls, and an unknown one is rejected. |
| The company is configured for e-invoicing and its sales documents reach the pools | My E-Invoice Admin | The search looks the shopper’s receipt up in the batch, single-general and individual pools, then in the to-IRB headers. A document that never reached a pool cannot be found. |
| Receipts carry a PIN, if you want the By Pin search | Printable format on the selling document | The PIN searched here is the one recorded on the document’s e-invoice pool entry. |
| An outgoing e-mail sender is configured | Messaging channel for the tenant | The guest tracking link is an e-mail. With no custom sender the platform default is used. |
Placing the widget
CP Commerce Admin → Website → (select website) → Layout Instance → (select layout) → Nodes → (select or add a node) → Main → Widget ID.
The Widget ID drop-down is built by enumerating the shared widget catalogue, so the entry reads EINVOICE WIDGET and its stored value is e-invoice. The storefront resolves that value to the widget.
Widget parameters
The widget reads six parameters from its node’s widgetPlaceHolderConfig.param. Four of them work and two are inert. None of the six has a field in CP Commerce Admin: the widget is registered in the catalogue without a parameter form and the node editor has none for it, so the only way to set them is to edit the layout’s raw JSON on the node’s Json Params tab — which edits the whole layout instance, not the single node. This is recorded as a product finding.
| Parameter | What it controls | Default | Effect when changed |
|---|---|---|---|
hideByPin | Whether the By Pin search sub-tab is offered | false — the tab is shown | Set true on a storefront whose receipts carry no PIN, so shoppers are not sent down a search that can never match. |
rejectionReasons | The radio list in the cancellation-reason pop-up | Wrong buyer details, Wrong invoice details, Others | Replaces the list entirely. Only the literal value Others opens the free-text box, so keep it in the list. |
einvoiceNoDisplayConfig | Which document number is displayed as the invoice number | server_doc_1 | Accepts server_doc_1, client_doc_1…client_doc_5, running_no, doc_reference. If the chosen field is empty on a record, the widget falls back to the invoice number. |
exportPdfPublicAccess | Whether the Export button appears on the search result for an unauthenticated visitor | false | Set true to let anyone who can produce the invoice number plus PIN (or amount and date) download the rendered e-invoice PDF. |
style | Declared as colour, font and background | — | Inert. The values are read but never applied; the widget’s colours are hard-coded. |
The public key is the whole guest path
The storefront has no anonymous session of its own. On start-up it looks its own hostname up and uses the hostname’s SYS_AKN_WEB_CP_COMMERCE_PUBLIC_KEY to authenticate every call it makes until someone signs in, at which point the user’s own sign-in takes over.
None of the calls in this flow needs the shopper’s own permissions — they need the storefront’s key. So a storefront whose hostname record is missing the public key shows the search form and fails every search with an authorisation error. That is the single most likely cause of “the e-invoice page does not work”.
The request window
The transaction-date picker in the By Date Amount search only accepts dates inside a window derived from the company’s e-invoice request extension period setting:
- No value set — only dates in the current calendar month are selectable.
- A day-of-month value, and today is on or before that day — dates from the start of last month onwards are selectable.
- A day-of-month value, and today is past it — only dates from the start of this month onwards.
This is a restriction on the date picker only. The search itself applies no date window.
Feature visibility / permissions
The widget gates its own features on one thing: whether the session holds an auth token. There are no client-side permission definitions for it, and no permission code is checked on the shopper’s behalf anywhere in this flow.
| Capability | Guest | Signed-in shopper |
|---|---|---|
| Search for a receipt | Yes | Yes |
| Request an e-invoice | Yes | Yes |
| Have the identity saved to a customer record | No | Yes |
| See Requested E-Invoices | Only through the e-mailed tracking link | Yes |
| See E-Invoice Cancellation and E-Invoice History | No | Yes |
| Request a cancellation | No — the effect chain is gated on an authorised session | Yes |
| Export the PDF from the search result | Only when exportPdfPublicAccess is true | Only when exportPdfPublicAccess is true |
| Export the PDF from the details pop-up | Through the tracking link | Yes |
Fields
Search — By Date Amount
| Field | Meaning | Required | Notes |
|---|---|---|---|
| Cash Bill/Invoice No. | The document number printed on the receipt | Yes | Matched against any of the numbers the receipt carries — its tenant, company or branch running number, or any of its client document numbers — so whichever one your printed receipt shows will do. The receipt is looked for in the three e-invoice pools first and then among e-invoices already built; the first match is taken. |
| Amount | The transaction total | Yes | Compared for exact equality with the document total — 12.5 and 12.50 both match, 12.49 does not. The form itself checks nothing, so a mistyped figure shows only as No matching eInvoice record found. |
| Transaction Date | The date on the receipt | Yes | Restricted by the request window above. Interpreted in Malaysian time. |
Search — By Pin
| Field | Meaning | Required | Notes |
|---|---|---|---|
| Pin | The PIN printed on the receipt | Yes | Matched against the PIN recorded on the document’s e-invoice pool entry, together with the invoice number as above. |
The search rejects anything that is neither (invoice number + PIN) nor (invoice number + date + amount) with “Invalid request. Provide either (invoice_no + pin_no) OR (txn_date + txn_amount + invoice_no).”, and returns “No matching eInvoice record found for the given criteria.” when nothing matches.
Update E-Invoice Profile
| Field | Meaning | Required | Notes / validation |
|---|---|---|---|
| Name | The buyer name that goes on the e-invoice | Yes | Also sent as the buyer’s customer code on the guest path. |
| Identification Type | The kind of identification document | Yes | Drop-down, exactly four values: PASSPORT, NRIC, BRN, ARMY. Nothing else can be chosen. |
| Identification Number | The identification document number | Yes | Must contain no whitespace — “ID must not contain spaces”. |
| TIN | Tax Identification Number | Yes | Must contain no whitespace — “TIN must not contain any space”. See Verify TIN below. |
| Where the e-invoice notification goes | Yes | On the guest path this is also where the tracking link is sent, and the address the tracking list is keyed on. | |
| Contact Number | Buyer phone | Yes | Must contain no - — “Contact Number no must not contain “-””. Placeholder shows the +60… form. |
| Address Line 1 | First line of the e-invoice address | Yes | |
| Country | Country | Yes | Drop-down from the country master; defaults to Malaysia. |
The form also asks for an optional SST Registration (the only optional identity field), Address Lines 2–5, Postal Code, City, a State chosen from the states of the selected country, and an optional Mobile Number (no -).
- For a signed-in shopper the form opens already filled from their customer record — name, TIN, SST number, identification, e-mail and phone — and from the customer’s address marked as the default e-invoice address, billing addresses first, then shipping. A guest starts from a blank form with Malaysia selected.
- Mobile Number does not reach the e-invoice. It is saved with the address. The buyer details written onto the receipt for a signed-in shopper carry the customer’s phone — the Contact Number — and an address block without the mobile number. Put the number LHDN should see in Contact Number.
- A buyer with no TIN of their own can enter one of the four general TINs LHDN publishes for that case (EI00000000010, EI00000000020, EI00000000030, EI00000000040). The widget accepts it as verified without asking LHDN, and Identification Number stops being required.
Verify TIN. The Update button is disabled until the Verify TIN button has returned a positive result for the exact combination of TIN, identification type and identification number currently in the form. The hint under the TIN field reads Verified in green or Not valid in red. Changing any of the three values after verifying invalidates the result and disables Update again.
What the identity becomes
| Form field | Signed-in shopper — written to the customer entity | Guest — sent in the request payload |
|---|---|---|
| Name | The customer’s name | The buyer name, also used as the buyer’s customer code |
| Identification Type | The customer’s ID type and its e-invoice ID type | The buyer’s e-invoice ID type |
| Identification Number | The customer’s ID number and its e-invoice ID value | The buyer’s e-invoice ID value |
| TIN | The customer’s e-invoice TIN | The buyer’s TIN |
| SST Registration | The customer’s SST number | The buyer’s SST number |
| The customer’s e-mail | The buyer’s e-mail | |
| Contact Number | The customer’s phone | The buyer’s phone |
| Address block | A customer address flagged as the default e-invoice address | A buyer address on the request |
A guest’s details are not saved anywhere as a customer record. They travel with the queue event and are written onto the document as the buyer JSON.
Lifecycle and effects
This is a request applet feature, not a document applet: nothing here creates a document line, moves stock or posts to the ledger. It changes how an existing sales document is submitted to LHDN, and it records one request.
Step 1 — BigLedger decides whether there is anything to do. Before queuing, it reads the pool entry named by the request and takes one of three courses:
| Course | When | Result |
|---|---|---|
| Normal request | The pool entry has not been submitted yet | The request is queued. |
| Request plus consolidated rebuild | A batch pool entry already submitted, whose e-invoice sits in the consolidated queue | The request is queued and a consolidated-queue rebuild is queued after it. |
| Nothing to do | A single-general or individual pool entry already submitted, or a batch pool entry submitted with nothing in a consolidated queue | The request is refused: “Request already made by someone or submitted as consolidated”. |
A pool GUID that does not exist raises “Batch Pool Container Not Found”, “Single General Pool Container Not Found” or “Individual Pool Container Not Found”. A signed-in request with no buyer entity GUID is rejected with “Entity hdr guid is null”, and one whose caller is not a login entity of that buyer returns not-authorised.
Step 2 — the queue. The request is queued and picked up straight away. It carries which pool entries are involved, the buyer (the signed-in shopper’s customer record, or the whole guest profile), the course chosen in step 1 and the storefront’s hostname.
Step 3 — what the queued run does. It branches on which pool the receipt sits in.
| Pool | What changes | The answer to “individual or consolidated” |
|---|---|---|
| Batch pool | The buyer is written onto the pool entry and onto the sales document, the pool entry is reset to unprocessed, and the document’s Submission Type is set to Individual. The pool entry is then processed into a submission-queue entry, and the resulting To IRB e-invoice is linked back to the document. | Individual. A receipt that would otherwise have been swept into the merchant’s monthly consolidated e-invoice becomes an individual e-invoice in the shopper’s name. Where the receipt had already reached a consolidated queue, a consolidated-queue rebuild is queued so the consolidated document is reconstructed without it. |
| Single general pool | The buyer is written onto the pool entry, and the pool entry and its document are updated as for any submission. | The submission the pool was already destined for, now carrying the buyer. |
| Individual pool | The same, on the individual pool entry. | As above. |
If the batch-pool branch produces no submission-queue entry, the request is recorded as FAILED rather than SUCCESS — and the receipt is stranded. Its batch pool entry is left processed and failed, with the document’s Submission Type already changed to Individual, so the monthly consolidation passes it by and no individual e-invoice was built: the sale is in neither the consolidated e-invoice nor an individual one until the merchant acts on that pool row (My E-Invoice Admin Applet, PROCESSED / FAILED).
SUCCESS means the e-invoice was built and queued for submission, not that LHDN has received it — the merchant’s ordinary submission run sends it, and its status is read on the e-invoice itself.
Step 4 — the request record. Either way one request record is created, carrying the request status, the sales document and its type, transaction date and amount, the invoice number, the PIN, the To IRB e-invoice, the five server and five client document numbers, and any validation message. For a guest it additionally carries the e-mail address and a 12-character access key — the first 12 characters of a random UUID.
Step 5 — the guest e-mail. When the row has both an access key and an e-mail, BigLedger sends one e-mail, subject “Trace your e-invoice request status”. On SUCCESS the body says the request has been processed and offers a Track Your Request link; on FAILED it says the request failed and to contact the relevant authority. The link first checks that the key and e-mail match a request record and then redirects the shopper to https://<website code>/page/E-Invoice?key=…&email=…. The widget reads those two query parameters and shows the guest’s requested-invoice list.
What the merchant sees. Every request is logged in the My E-Invoice Admin Applet under Internal Submission → Portal Request — one record per request, SUCCESS or FAILED, whether it came from this widget or from the customer portal, which runs the same processor. The request also surfaces as a changed pool row and a new submission-queue entry, with the buyer’s name, identification, TIN and address now on the document. There is no approval step and no notification to the merchant that a shopper asked: Portal Request is the list to read, and a FAILED row on it is the one that needs somebody.
Related applets
- CP Commerce Admin — where the widget is placed on a page, and where every other storefront setting lives.
- My E-Invoice Admin — the merchant’s side of the pipeline this feeds: the pools, the submission and validation queues, the cancellation requests.
- MY E-Invoice Portal — the same request made from a tenant-side buyer account rather than a storefront. Different applet, same underlying records.
- Customer — where a signed-in shopper’s identification, TIN and default e-invoice address are stored and can be corrected.
- POS General — the cash bill most storefront e-invoice requests are about, and where its PIN comes from.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| Every search fails, with an authorisation error or nothing at all | The storefront’s hostname record has no SYS_AKN_WEB_CP_COMMERCE_PUBLIC_KEY, so the app cannot authenticate any call | Add the public key extension parameter to the Host Name record for that domain. The app logs a configuration error naming Public Key on start-up when it is missing. |
| “No matching eInvoice record found for the given criteria.” on a receipt the shopper is holding | The search is exact on all supplied values. Amount must match the pool entry exactly; the date is taken in Malaysian time; the invoice number is matched against every running and client number on the receipt, so check the amount and the date first | Try the By Pin search instead, which matches on invoice number and PIN only. If the document never reached a pool, it will never be found here — check the posting queue on the merchant side. |
| The transaction date the shopper needs cannot be selected | With no extension period set, the picker allows the current month only. With one set, it allows every date from the start of last month onwards while today is on or before that day of the month, and from the start of this month onwards after it — there is no upper bound either way | This is the configured request window. Set or widen the extension period in the company’s e-invoice settings, or use the By Pin search, which has no date field. |
| “This invoice has already been submitted. No further requests can be made.” | Either the record is already submitted, or the search matched an e-invoice already sent rather than a pool entry, so there is nothing left to request against | Expected once an e-invoice exists. If the shopper’s details are wrong on it, the remedy is a cancellation request, not a second request. |
| A request came back Validation Error and the shopper wants to correct it and ask again | They cannot. Once a pool entry has been submitted, BigLedger refuses a second request against it, whichever pool it sits in; the search then matches the sent e-invoice and shows the amber banner with no Request button | Only the merchant can correct and resubmit it, and only from To IRB E-Invoice in the My E-Invoice Admin Applet — the successful submission already removed the pool entry. A shopper whose request failed (counted under In Queue for the batch and single-general pools) can correct and request again, because the pool entry never reached success. |
| “Request already made by a user.” | Shown for every failure of the request call — a genuine duplicate, an authorisation failure, and a network error all produce these words | Treat it as “the request did not go through”, not as proof of a duplicate. Check the merchant-side pool row before telling the shopper it already exists. |
| Update stays greyed out in the profile pop-up | Verify TIN has not returned a positive result for the exact TIN, identification type and identification number now in the form | Press Verify TIN. If it returns Not valid, the combination was rejected upstream — the identification number and TIN must belong together. |
| The form will not accept a TIN or identification number | Both reject any whitespace, including a trailing space pasted from a document | Remove spaces. The same applies to - in the phone and mobile fields. |
| Request for Rejection does nothing in list view | The pop-up is opened as a different kind of dialog from the list view than from the grid view, and the component only works in the grid view’s form | Switch to grid view with the toggle at the top of the list. Recorded as a product finding. |
| A signed-in shopper’s other billing addresses disappear after they update their e-invoice profile | The profile save builds the billing-address array from the shipping addresses when both exist | Recorded as a product finding. Check and repair the customer’s addresses in the Customer applet after a shopper updates their profile. |
| A guest never receives the tracking e-mail | The e-mail is only composed for request statuses SUCCESS and FAILED; a status outside those two sends an empty message, and any mail failure is silent | Look the request up on the merchant side by invoice number. Recorded as a product finding. |
| The widget cannot be configured from CP Commerce Admin | It is registered in the shared catalogue with no parameter form, and the applet has no editor for it | Edit the layout JSON on the node’s Json Params tab. Recorded as a product finding. |
Related documentation
- CP Commerce Admin applet — the console this widget is placed from.
- Push Notification Configuration — the other CP Commerce feature documented screen by screen.
- E-Invoice module — how the Malaysian e-invoice pipeline fits together.
- E-Commerce module — the storefront this widget lives in.