Skip to main content
Bank Transfer 3.0.x charges fees through the standalone Fees plugin, plugin-fees. Bank Transfer 3.1.0 and later can charge them through the Fees Engine that runs inside Midaz 4.1.0 and later. This page moves your fee packages from plugin-fees to Midaz. It then switches Bank Transfer over in an order that keeps every transfer charged. Follow this page when all of these are true:
  • You run Bank Transfer 3.0.x.
  • Your fee packages live in plugin-fees.
  • You upgrade to Bank Transfer 3.1.0 or later and to Midaz 4.1.0 or later.
Bank Transfer uses only the fee packages of plugin-fees. If you also keep billing packages in plugin-fees, recreate them as Midaz billing packages before you retire plugin-fees. This page does not map them.
The Lerian Console manages Midaz fee packages only. It does not show the packages that live in plugin-fees. See Console.

How Bank Transfer chooses who charges the fee


From 3.1.0, the Bank Transfer variable MIDAZ_FEE_MODE decides who calculates and charges the fee of each transfer.
  • Bank Transfer does not start with any other value.
  • Under auto, Bank Transfer reads the Midaz version again every MIDAZ_FEE_MODE_REFRESH (default 5m). When it cannot read the version, it keeps the mode it has, or uses legacy if it has none.
  • Bank Transfer fixes the mode of a P2P transfer or TED OUT at /initiate, and of a TED IN when it receives it. Every later step of that transfer uses the same mode: retries, confirmation, cancellation and reconciliation. A change of mode applies to new transfers only.
  • In legacy mode, BTF_FEE_ENABLED turns the plugin-fees integration on. With false, the default, Bank Transfer does not call plugin-fees, and every transfer runs with fee 0.
  • Set native only on Midaz 4.1.0 or later. Midaz 4.0.x charges fees without marking the fee entries, so Bank Transfer cannot read the fee that it charged.
Do not rely on auto during the migration. Under auto, Bank Transfer 3.1.0 moves to native by itself as soon as it reads Midaz 4.1.0 or later. New transfers then use the Midaz packages. If your packages are still only in plugin-fees, Midaz finds no package, and those transfers carry no fee.In Bank Transfer 3.1.0, BTF_FEE_ENABLED does not stop Midaz from charging fees in native mode. To stop a fee, disable its Midaz package with enable: false, or delete the package. Midaz then also charges no fee on a native transfer that it has not posted yet.

Before you start


You need:
  • Midaz 4.1.0 or later, or a plan to upgrade to it. See Updating Midaz.
  • Bank Transfer 3.1.0 or later, or a plan to upgrade to it.
  • Access to the plugin-fees API, to read and estimate your packages.
  • Access to the Midaz fee package API, or to the Console Fee Packages page.
  • For each ledger and transfer type, the Midaz transaction route that Bank Transfer posts with. The tenant policy routing.ledger_bindings names it. See Ledger routing.
  • Three Midaz permissions for the Midaz credentials of Bank Transfer: the resource packages with action get, the resource estimates with action post, and the resource organizations with action get. In single-tenant mode, these are the credentials of MIDAZ_CLIENT_ID. In multi-tenant mode, Bank Transfer uses the Midaz credentials of each tenant, so grant all three permissions to the application of each tenant.
legacy mode does not use these permissions. In native mode, without packages, every P2P and TED OUT initiation answers 503 BTF-2000. Without estimates, every initiation that matches a package answers 503 BTF-2000. organizations is needed because this page sets MIDAZ_FEE_MODE=native explicitly: Bank Transfer then makes one read of the Midaz organization list, to check the /v2 API, before it first uses a set of credentials. Under auto, it does not make this read. Without organizations, Bank Transfer in single-tenant mode does not start, and in multi-tenant mode every P2P and TED OUT initiation of that tenant answers 503 BTF-2000. The examples below use these shell variables. Every UUID is a placeholder. Use the values from your own environment. Bank Transfer reaches the Midaz ledger at MIDAZ_TRANSACTION_URL, or at MIDAZ_BASE_URL when that is unset. It strips a trailing /v1 or /v2 from that address. Strip it in MIDAZ_LEDGER_URL too, because the paths below carry the version.

Map a plugin-fees package to a Midaz package


Migrate only the packages that are enabled in plugin-fees. Leave the disabled ones out. Midaz refuses a package whose amount range overlaps another package with the same route and segment, and it counts disabled packages too (error 0199). So a disabled copy can block a package that you need. The fee rules keep their field names and their JSON shape. Three things change: where the organization goes, where the ledger goes, and what transactionRoute holds. Midaz rejects a field that it does not know. Send none of ledgerId, id, createdAt, updatedAt and deletedAt. A TED OUT charges its fee on top of the amount. In native mode, Bank Transfer refuses a TED OUT with 422 BTF-3002 when the package that matches it has a fee with isDeductibleFrom: true.

Transaction route

plugin-fees compares transactionRoute with a name that Bank Transfer sends: ted_out, ted_in or p2p. Midaz compares it with the routeId of the transaction, which is the ID of a Midaz transaction route. Midaz accepts only a UUID in this field. For each package, use the transaction route that routing.ledger_bindings names for the ledger and the transfer type of the package.
A Midaz package charges every /v2 transaction of its ledger that matches its scope, from any product, not only the transfers of Bank Transfer. A package with no transactionRoute matches every transaction. A package with a route matches every transaction posted with that route. Give each Bank Transfer package its transaction route, and do not use those transaction routes in other products.A ledger whose binding uses mode: omit has no safe package: the only one that can match its transfers has no route, so it charges every /v2 transaction of the ledger. Give that binding the Midaz transaction route of each transfer type on the Console Accounting Routes page, and check Cutover readiness there. This changes the accounting of every Bank Transfer posting on that ledger. Do it after step 2, once Midaz runs 4.1.0 or later, and before you switch.

Package scope

plugin-fees and Midaz select the package of a transfer in different ways, so an exact copy can charge a different fee. Apply these rules to the enabled packages of each ledger:
  • The ledger has one enabled package. plugin-fees applies it to every transfer in its amount range, P2P, TED OUT and TED IN alike. It ignores the transactionRoute and the segmentId of that package. In Midaz, create one copy for each transfer type that it charged, each with the transaction route of that type, and without segmentId.
  • A route has one package. When the ledger has more than one enabled package, plugin-fees applies the only package of a route to every transfer on that route, inside its amount range. It ignores the segmentId of that package. Create the Midaz package without segmentId.
  • A route has several packages. plugin-fees then picks by the segment of the sender. A sender with a segment gets only a package with that segment. A sender with no segment gets only a package with no segment. Keep each segmentId. Midaz also applies a package with no segment to a sender who has a segment, when no package of that segment covers the amount. If the route has a package with no segment, decide which behavior you want. When every fee of that package is charged on top of the amount (isDeductibleFrom: false), you can keep the plugin-fees behavior. Add segment:<segment-uuid> to the waivedAccounts of that package for every segment of the ledger, and for every segment that you create later. Midaz then charges none of its fees to a sender in those segments. On a fee taken out of the amount, the waiver also exempts a recipient in those segments, so do not use it there. For such a package, the engines cannot match. Decide whether a sender whose segment has no package of its own for that amount pays it in Midaz, or whether you leave it out, so that a sender with no segment pays none of its fees.
  • TED IN. For a TED IN, Bank Transfer sent plugin-fees the segment of the recipient account. Midaz reads the segment from the source accounts, and it skips the external account, which is the only source of a TED IN. So a Midaz package with a segmentId never matches a TED IN. Create every TED IN package without segmentId. If the ledger has more than one TED IN package in plugin-fees, plugin-fees charged a recipient with a segment only a package with that segment, and a recipient with no segment only a package with no segment. Midaz cannot see the segment of the recipient, and it applies the packages with no segment to every recipient. Decide the TED IN price that applies to every recipient.
  • A package with no route. When the ledger has more than one enabled package, plugin-fees never applied a package with no route to a Bank Transfer transfer, because Bank Transfer always sends a route. Leave it out.
  • Two packages that match. Midaz selects the package that matches the most constraints. When two packages match equally, Midaz refuses the transaction with error 0198. At initiation, Bank Transfer answers 422 BTF-2005.

Migrate step by step


1

Pin the legacy mode

Set MIDAZ_FEE_MODE=legacy in the Bank Transfer environment. In the Helm chart, the key is bankTransfer.configmap.MIDAZ_FEE_MODE, and the chart renders auto when the key is unset. Bank Transfer 3.0.x ignores the variable, so you can set it ahead of the upgrade.Check the value that your deployment renders before you upgrade, for example with helm template or helm diff. A rolling update does not stop after its first pod unless you pause it.Keep BTF_FEE_ENABLED=true and the FEES_* variables as they are.
2

Upgrade Bank Transfer, then Midaz

Upgrade Bank Transfer to 3.1.0 or later. Wait until every Bank Transfer pod runs 3.1.0 or later. An earlier version cannot settle a transfer that Bank Transfer created in native mode.Check the log of every pod for midaz: fee mode resolved with feeMode=legacy and source=config. In single-tenant mode, a pod logs it at boot. In multi-tenant mode, it logs it for each tenant at the first transfer of that tenant. source=config proves that the pin works. Check it while Midaz still runs a version earlier than 4.1.0. There, auto also resolves to legacy, so a transfer that pays its plugin-fees fee does not prove the pin.Upgrade Midaz to 4.1.0 or later.Make a small transfer. Check that plugin-fees still charges its fee.
3

Recreate each enabled package in Midaz

List the enabled packages of each ledger in plugin-fees. Each answer holds one page, and its total counts the items of that page only. Read the next page until a page returns fewer items than limit. The list leaves out deleted packages.
Create each package in Midaz with the mapping and the scope rules above. Set enable to false. Keep a list of the packages that you create: you enable exactly these when you switch. This example recreates a flat TED OUT fee. Its transactionRoute is the TED OUT transaction route of the ledger binding.
You can also create the packages on the Console Fee Packages page.
4

Compare the fees of both engines

For each package, estimate the same transaction in plugin-fees and in Midaz. Compare the fee amounts. They must be equal. Use amounts at both ends of the range of the package, and one amount in the middle. For a package that carries the segment waivers from Package scope, use a sender outside those segments. Midaz exempts a sender in them by design, and plugin-fees does not.
In the Midaz answer, each fee entry has "feeLeg": "true" in its metadata. You can also use the Console Fee Calculator.An estimate calculates the one package that you name. It does not check transactionRoute or segmentId, so it does not show which package a transfer gets. The next step tests that.
5

Rehearse the selection in homologation

Run steps 1 to 4 in homologation first, with the same packages. Then test which package each transfer gets, before you switch production:
  1. Under legacy, call POST /v1/transfers/initiate for each case: P2P and TED OUT, from a sender in each segment of the ledger and from a sender with no segment, with amounts at both ends of the range of each package. Record feeAmount and packageAppliedId from each answer. Do not process these initiations. An initiation posts nothing to Midaz. It writes a payment_initiations row that expires at expiresAt, keeps a duplicate fingerprint for 300 seconds by default, and sends a payment_initiation.created event to your webhook and event consumers. A TED OUT initiation outside the operating window answers 422 BTF-0010.
  2. Switch homologation to native, as in the next step.
  3. Repeat the same initiations. Compare each feeAmount. packageAppliedId now names the Midaz package. Check that it is the package that replaces the plugin-fees one. An identical initiation inside that duplicate window answers 409 BTF-0012, so wait for it to pass.
  4. TED IN has no initiation. Under each mode, receive one small TED IN for a recipient in each segment and for a recipient with no segment. Compare the fees.
Expect a difference only where Package scope says the engines cannot match, and only the one you chose there. Fix every other difference in the Midaz packages, and repeat the cases.
6

Switch Bank Transfer to native

Before the switch, run the P2P and TED OUT initiation cases of step 5 in production, under legacy. Use test accounts of your own, one in each segment and one with no segment. Record each feeAmount. Step 5 says what an initiation writes.
  1. Give the Midaz credentials of Bank Transfer the packages (get), estimates (post) and organizations (get) permissions.
  2. Enable each Midaz package that you created in step 3. Send "enable": true with Update a Package. No initiation tests a TED IN, so first compare the transactionRoute of each TED IN package with the Transaction route of the TED_IN binding of its ledger, on the Console Accounting Routes page.
  3. Set MIDAZ_FEE_MODE=native.
  4. Restart every Bank Transfer pod.
Right after the restart, run the same cases again, with the same accounts. Compare each feeAmount, and check each packageAppliedId. Expect a difference only where Package scope says the engines cannot match, and only the one you chose there. An identical initiation inside the duplicate window answers 409 BTF-0012, so wait for the window to pass between the two runs. An initiation posts nothing to Midaz, so these cases show a wrong route or segment ID without moving money.If another difference shows, roll back. Correct the fees or the amount range of a package with Update a Package. Update a Package cannot change transactionRoute or segmentId, so for a wrong route or segment, create a corrected package with enable: false, and delete the wrong one only after the native transfers that used it have finished. This query lists the transfers created in native mode since the restart began. Check the fee of each one by hand.
New transfers now use Midaz fees. A P2P transfer or TED OUT initiated before the restart, and a TED IN received before it, keep the legacy mode until they finish.
7

Check real transfers

Make a small P2P transfer or TED OUT. Check the fee that Bank Transfer shows at initiation. Check the fee that it records on the transfer. Both must match the fee that plugin-fees charged before, except for a difference that you chose in Package scope.Check the first TED IN that arrives after the switch the same way.In Midaz, the metadata of each transaction has packageAppliedID. It is the ID of the Midaz package that charged the fee.
8

Retire plugin-fees

Keep plugin-fees running while this query returns rows. In multi-tenant mode, run it on the Bank Transfer database of each tenant.
These are TED INs received before the switch. When Bank Transfer resumes one, it asks plugin-fees for its fee. If plugin-fees is stopped, Bank Transfer credits the recipient with no fee. When fees.fail_closed_default is true, it returns the TED to the sender instead, unless Bank Transfer cannot read that policy.Then back up the plugin-fees MongoDB database, and stop plugin-fees. After that, you cannot roll back.Keep MIDAZ_FEE_MODE=native. Under auto, a pod that cannot read the Midaz version starts in legacy mode, and legacy needs plugin-fees.

Roll back


You can return to plugin-fees while plugin-fees still runs with its packages unchanged:
  1. Set MIDAZ_FEE_MODE=legacy.
  2. Restart every Bank Transfer pod.
New transfers then use plugin-fees again. This needs BTF_FEE_ENABLED=true and the FEES_* variables still in place. Transfers created in native mode keep that mode. Bank Transfer finishes them on the Midaz /v2 API, and Midaz charges their fees when it posts them. Keep the Midaz packages enabled until every native transfer has finished. Keep Bank Transfer on 3.1.0 or later while any transfer created in native mode is still open. An earlier version settles that transfer on /v1, so its fee is not charged or not recorded.

Console


The Fees Engine pages of the Console, Fee Packages and Fee Calculator, work on Midaz fee packages only. They do not read plugin-fees.

See also