Skip to content
FPX E-Mandate API

FPX E-Mandate API

By the end of this page you can enrol a customer in FPX direct debit from your own front end: raise the mandate, send the customer to their bank to approve it, receive the bank’s answer, and keep the mandate’s status current afterwards. BigLedger has no screen for any of this. The whole capability is the eight endpoints below. Everything a customer sees during enrolment is yours to build.

An e-mandate is a customer’s standing authorisation, given through their own bank, for you to debit their account directly. In Malaysia that authorisation is granted over FPX (Financial Process Exchange), the national payment gateway. BigLedger stores the resulting mandate against the customer entity it belongs to.

Plan for three things the platform will not do. It will not chase a mandate the bank has not confirmed. You have to read it to refresh it (see Lifecycle and effects). It will not list mandates on any report. And it gives you no read-only role: anyone who can look at a mandate can also raise one (see Permissions).

Where it fits

UpstreamThisDownstream
A settlement method of type FPX_EMANDATE, carrying the FPX seller credentials and signing certificate. Settlement methods are documented on the Cashbook applet pageThe mandate record on the customer entityCollections taken against the mandate, settled through the Cashbook applet
A customer entity in Customer Maintenance. The mandate hangs off its entity line

All eight routes sit under /core2/tnt/dm/paygw/my/fpx/ (the paygw family, see API Reference). None carries an etl-ep or backoffice-ep segment, so the access-key suffix rules on the ERP Core API page do not apply. The two confirmation callbacks take no token at all.

Where a mandate shows in the product

Only in one place. A customer’s record in Customer Maintenance lists FPX e-mandate among the customer’s settlement methods, and nothing behind it shows what was stored. If your users need to see a mandate’s status, your front end has to show it.

The unpublished front ends you may be handed

Two e-mandate screens exist in BigLedger’s repositories, and neither can be installed. If someone gives you one as a starting point, know what it does:

  • The e-mandate applet (blg-applet-core-akaun-platform-e-mandate-applet) has no registry row. Its sidebar reads E-Mandate Create · E-Mandate Update · E-Mandate Terminate, but only the create form is real (Online Direct Debit Enrolment, one Details tab), and only its CREATE button is wired. Save & Email, Save As Draft and Cancel do nothing. Update and Terminate are one component, GenericContainerComponent, that lists internal sales orders through InternalSalesOrderService. It is not a mandate list. The create action does not call the FPX endpoints on this page. It posts the raw form to a payment-gateway init-form service, and the object it builds for FPX is thrown away. Its Field Settings and Default Selection screens save nothing.
  • An older Admin applet (in blg-applets-core-akaun-platform) carries an Online Direct Debit menu with listing, create, edit, terminate and status screens that do call these endpoints. Its only registry row is deleted, and its e-mandate screens were last changed in October 2021.

Treat both as reference code, not as a product. The capability that is real is the API.

Configuration

Everything that has to be right before a mandate can be created lives in another applet, on the settlement method. There is no e-mandate setting anywhere else: the unpublished front end’s settings screens save nothing, and since it cannot be installed there is nowhere a setting could be stored.

Before you can use it

  • A settlement method whose reference is FPX_EMANDATE, carrying two extension rows — FPX_SELLER_ID and FPX_EXCHANGE_ID. A missing method fails with NO_FPX_STL_MTHD_FOUND; a missing extension with NO_FPX_STL_MTHD_DETAILS_FOUND.
  • A signing certificate on that settlement method. Upload it through POST /core2/tnt/dm/erp/settlement-methods/current-fpx-public-cert-upload/{hdrGuid}; the next certificate in a rollover goes to …/next-fpx-public-cert-upload/{hdrGuid}.
  • The buyer’s bank must exist in bl_fi_bank_hdr with country_code = MYS and a bank code that matches fpx_buyerBankId, or the request fails with Invalid buyer bank id. Fetch the live list from GET /core2/tnt/dm/paygw/my/fpx/banks.
  • A payment reference number — GET /core2/tnt/dm/paygw/my/fpx/pmt-ref-no increments the tenant’s SETTLEMENT-METHOD-FPX-EMANDATE sequence and returns the next value.

Permissions

Every authenticated FPX endpoint — the AD request and all three read endpoints — accepts the same set: the caller is a tenant OWNER or ADMIN, or holds one of API_TNT_DM_PGW_FPX_OWNER, API_TNT_DM_PGW_FPX_ADMIN or API_TNT_DM_PGW_FPX_CREATE.

API_TNT_DM_PGW_FPX_MEMBER is defined in TntPgwPermissions but is not accepted by any endpoint on this controller, so there is no read-only role for mandates: anyone who may look at a mandate may also raise one.

The two AC callbacks take no token at all.

Fields

There is no working create form, so “the fields” here means two different things: the rows a mandate is made of once it exists, and the fields the AD request must carry to create one. Both are below. The form in the unregistered front end asks for a third, longer set — reference number, signatory, initial collection option, collection dates — and discards most of it; see The unpublished front ends.

How a mandate is stored

A mandate is not a document. It has no header, no lines, no signum, and it posts nothing to the ledger. It is a set of rows on the customer’s master-entity record (EMandateContainer):

RowWhat it holds
bl_fi_mst_entity_lineThe mandate itself — one row per mandate, hanging off the customer entity
bl_fi_mst_entity_ext with param_code = PMT_MTHD_E_MANDATEThe FPX request message as JSON (buyer name, e-mail, bank, ID, IBAN-encoded terms, amount, product description), and the mandate’s status
bl_fi_mst_entity_ext with param_code = BL_FI_MST_ITEM_HDR_GUIDThe link to the FPX settlement method that supplies the seller credentials and the signing key
bl_fi_mst_entity_eventOne row per exchange with FPX — NEW_APPLICATION_AD_REQ, NEW_APPLICATION_DIRECT_AC, NEW_APPLICATION_INDIRECT_AC — each carrying the message sent or received

Because the terms live in the ext row’s JSON rather than in columns, there is no BigLedger-side validation of them beyond the checks listed under Creating a mandate.

What the request must carry

Every one of these is checked before anything is saved, and each throws its own message naming the field:

FieldMeaning
fpx_msgToken01 consumer, 02 or 03 business
fpx_sellerOrderNoYour reference for this mandate
fpx_txnAmountThe ceiling per collection. Must be greater than zero
fpx_buyerEmail, fpx_buyerName, fpx_buyerIdThe payer and their identity document number
fpx_buyerBankIdThe bank code from the FPX bank list
fpx_productDescWhat the collection is for
fpx_buyerIbanThe packed terms string — see below

fpx_buyerIban is unpacked into six values, four of which are validated: application type must be 01, 02 or 03; phone number must be present; maximum frequency must be present and greater than zero; frequency mode must be present (DL daily, WK weekly, MT monthly, YR yearly); effective date must be present. The expiry date is carried but not checked.

Three values are fixed by BigLedger and cannot be set per request: seller bank code 01, currency MYR, and FPX message version 7.0. A mandate in any other currency is not supported.

Lifecycle and effects

A mandate is not a document. It has no document type, no amounts on a ledger and no document number, so none of the usual document machinery touches it: no journal line, no stock movement, no e-Invoice, no knock-off, no VOID. Its whole life is the two-step exchange below, and every step of that exchange happens inside the HTTP request that triggers it.

Nothing sweeps a pending mandate. There is no background job for FPX or for mandates of any kind — no queue, no retry and no scheduled reconciliation with FPX. A mandate whose confirmation never arrives stays at NEW_APPLICATION_PENDING_AUTHORIZATION for as long as nobody looks at it, and it will not appear on any report, because no report lists mandates.

The one thing that moves it is a read: GET …/fpx/e-mandate-event/{eventGuid} asks FPX for the current status and writes the answer back when the stored event is still ACTIVE or PENDING_AUTHORIZATION. Reconciling mandates is therefore something a person or an integration has to do on a schedule of its own — the platform will not do it for you.

The AD / AC exchange

Every change to a mandate — creating it, amending it, ending it — is the same two-step exchange with FPX. BigLedger builds and signs an AD (authorisation request) message; FPX replies with an AC (authorisation confirmation) message, either straight back to the server or through the customer’s browser.

  1. You post the mandate to POST /core2/tnt/dm/paygw/my/fpx/ad-request-message with an EMandateContainer body. BigLedger validates it, saves it if this is a new application, records an AD_REQ event, and returns the signed request message.

  2. The signed message is submitted to FPX, which takes the customer to their own bank to approve it.

  3. FPX confirms, by one of two routes:

    • POST /core2/tnt/dm/paygw/my/fpx/direct-ac/{tenantCode} — server to server. Returns JSON.
    • POST /core2/tnt/dm/paygw/my/fpx/indirect-ac/{tenantCode} — the customer’s browser is redirected here. Returns HTTP 302 to a status page.

    Both are anonymous endpoints — no login token. They are authenticated by the fpx_checkSum signature on the message, and a mismatch fails with CLIENT_FPX_CHECKSUM_VERIFICATION_FAILED.

  4. BigLedger updates the mandate from the confirmation: it stamps the AD event with the outcome, records the AC as a second event, and rewrites the mandate’s status or terms.

What the three application types do

The type is not a separate field — it is the first two characters of fpx_buyerIban, which is a packed string rather than a bank account number.

CodeTypeEffect on a successful confirmation
01New applicationThe mandate row is created before the AD request is sent, then its status is set to NEW_APPLICATION_<outcome>
02MaintenanceThe stored e-mail, amount and terms are overwritten from the request. The status is not changed — a maintained mandate keeps whatever status its new application left it with
03TerminationThe status is set to TERMINATION_<outcome>, but only if the outcome was not UNSUCCESSFUL

Maintenance and termination both require the mandate to already exist; if it does not, the request fails with EMANDATE_GUID_DOES_NOT_EXIST.

The outcome comes from one FPX field

fpx_debitAuthCode on the confirmation decides everything:

fpx_debitAuthCodeOutcome
00SUCCESSFUL
99PENDING_AUTHORIZATION
anything elseUNSUCCESSFUL

Creating a mandate

Creating, amending and terminating a mandate are the same exchange with a different application type in fpx_buyerIban, and all three are API calls — there is no working screen for any of them. Before the first one will succeed you need the settlement method and certificate under Before you can; the body has to carry the fields under What the request must carry.

For a new application (01) the mandate rows are written to the customer entity before the AD request is signed, so a customer whose bank later declines still has a mandate row — at NEW_APPLICATION_UNSUCCESSFUL. Nothing removes it. For maintenance (02) and termination (03) the mandate must already exist or the call fails with EMANDATE_GUID_DOES_NOT_EXIST.

Statuses

Two different status values are in play, and they are easy to confuse.

On the mandate (bl_fi_mst_entity_ext.status) — the application type and the outcome, joined with an underscore:

StatusWhat it means
ACTIVEThe row exists but no FPX exchange has completed against it
NEW_APPLICATION_PENDING_AUTHORIZATIONSent to the bank; the customer has not finished approving
NEW_APPLICATION_SUCCESSFULAuthorised. This is the state a mandate you can collect against is in
NEW_APPLICATION_UNSUCCESSFULThe bank or the customer declined
TERMINATION_PENDING_AUTHORIZATIONTermination sent, not yet confirmed
TERMINATION_SUCCESSFULEnded

On each event (bl_fi_mst_entity_event.status) — ACTIVE, SUCCESSFUL, PENDING_AUTHORIZATION, NO_TRANSACTIONS_FOUND or UNSUCCESSFUL.

There is no expiry status and no rejection status. An expiry date is carried inside the packed fpx_buyerIban string and is enforced by the bank, not by a BigLedger status transition; a decline is …_UNSUCCESSFUL.

Reading a mandate can move it. GET …/fpx/e-mandate-event/{eventGuid} does not simply return what is stored: if the underlying AD event is still ACTIVE or PENDING_AUTHORIZATION, BigLedger asks FPX for the current status (an AE enquiry) and writes the answer back before replying. A read of a pending mandate is therefore also a refresh of it.

The two applets you set up first

A mandate is not a document, so there is no document before it and none after it. Two applets own the halves of it, and someone has to work in both before your first AD request will succeed.

AppletWhy you end up there
Customer MaintenanceThe mandate is rows on a customer’s entity record, so every mandate starts as a customer created here. This is also the only screen in the product where the existence of a mandate shows: FPX e-mandate sits among the customer’s settlement methods, with nothing behind it explaining what was stored.
CashbookSettlement methods are documented on the Cashbook page, and the FPX_EMANDATE method is what carries the seller id, the exchange id and the signing certificate. Without it the AD request fails at the first check with NO_FPX_STL_MTHD_FOUND. Collections taken against a mandate also settle through the cashbook.

Nothing else reads a mandate. It posts no journal, touches no document, and no report in the product lists mandates — which is why the only way to see one today is to read the customer’s entity rows.

Troubleshooting

SymptomCauseFix
bl_fi_mst_entity_ext for PMT_MTHD_E_MANDATE not foundThe container was posted without the ext row that holds the FPX messageBuild the container with both ext rows — the mandate JSON and the settlement-method link
bl_fi_mst_entity_ext for BL_FI_MST_ITEM_HDR_GUID not foundThe mandate is not linked to a settlement methodAdd the link ext row before requesting the AD message
NO_FPX_STL_MTHD_FOUND / NO_FPX_STL_MTHD_DETAILS_FOUNDThe linked settlement method is missing, or has no FPX_SELLER_ID / FPX_EXCHANGE_IDCreate the FPX_EMANDATE settlement method and fill both extension values
Invalid buyer bank idfpx_buyerBankId does not match a Malaysian bank rowTake the code from GET …/fpx/banks, not from the customer
CLIENT_FPX_CHECKSUM_VERIFICATION_FAILED on a callbackThe certificate used to sign does not match the one FPX verified againstCheck which certificate the settlement method is serving — current or next
EMANDATE_GUID_DOES_NOT_EXIST on an amend or terminateApplication type 02 or 03 sent for a mandate that was never createdCreate the mandate with type 01 first
A mandate sits at NEW_APPLICATION_PENDING_AUTHORIZATIONThe customer has not finished approving at their bank, or FPX has not confirmedRead the event — the read triggers an enquiry to FPX and writes back the current answer

Related documentation

Last updated on