MevvPos documentation

One engine: Turkish bank virtual POS and the international providers.

MevvPos is two things in one plugin. In Türkiye it drives your own bank virtual POS — several accounts side by side, each card routed by its BIN to the bank that issued it, instalments and per-instalment commission. Everywhere else it can connect to Stripe, PayPal, Mollie, Square, Authorize.Net, Checkout.com, Razorpay and Paystack over their own hosted pages. This page covers installation, both WooCommerce checkout surfaces, every admin screen, webhook setup, what the free edition does, what Pro adds, and what the plugin deliberately does not do.

Installation

MevvPos needs WordPress 6.0+ and PHP 8.1+. WooCommerce is optional. With WooCommerce 9.0 or newer active, MevvPos registers itself as a checkout payment method; without it the payment engine still loads and can be driven by another plugin. Earlier versions declared WooCommerce as a hard dependency and WordPress refused the activation without it — that requirement is gone.

Both WooCommerce checkout surfaces are covered. MevvPos registers itself for the classic shortcode checkout and for the newer Checkout block. Until 4.7.1 only the classic page was wired up, so a store that moved to the block checkout lost the card option outright — no error, no warning, just a missing payment method. Nothing extra needs setting: on the block page the card fields, the instalment table and the instalment surcharge all appear the same way.

  1. Upload the plugin under Plugins → Add New → Upload Plugin and activate it.
  2. Open MevvPos in the left admin menu. There is also a shortcut under WooCommerce, where merchants tend to look for payment settings.
  3. On the General tab, switch the payment method on and set the title the customer will see at checkout.
  4. On Bank API, choose your provider — a Turkish bank virtual POS or one of the international providers — and enter the credentials it gave you.
  5. If you chose an international provider, copy the Webhook address printed on that same tab into the provider’s own panel. Nothing completes without it.
  6. On Instalments and commission, enter your instalment rates.
  7. Test with your bank’s test credentials before going live.

The free plugin creates no database tables and registers no scheduled job. Everything lives in WordPress options and in order meta.

Pro is a separate plugin, installed alongside the free one — not a replacement for it. The free plugin ships on WordPress.org, where anything published can be redistributed under the GPL; keeping the paid source in the same package would have made it legally redistributable by anyone who deleted the licence checks. The licence key is entered on the Licence tab.

Opening WooCommerce → Settings → Payments → MevvPos redirects you to the MevvPos screen. That is deliberate: there is one place for these settings, not two that can disagree.

How it works

MevvPos speaks two kinds of payment protocol, and which one you get follows from the provider you picked. Both go through the same engine and produce the same order record.

Signed form — Türkiye
The seven Turkish virtual POS families. MevvPos builds the payment fields, signs them with your store key and posts them to the bank. The customer authenticates with 3-D Secure and the bank returns to your site; the signature on that return is what completes the order.
Hosted page and webhook — international
Stripe, PayPal, Mollie, Square, Authorize.Net, Checkout.com, Razorpay and Paystack. MevvPos creates the payment at the provider over its REST API and sends the customer to the provider’s own page. The order is completed by an answer from the provider: the signed notification that reaches your webhook address, or — if the customer gets back before it arrives — a server-to-server status query made from the return page. What never completes an order on its own is the browser’s return.

On the Türkiye side the engine does something the international providers never need: it decides which of your accounts takes the card.

  1. You define one or more POS records. A POS record is one bank’s virtual POS: its credentials, its gateway address, its instalment rates.
  2. At checkout the customer types their card. The first six digits — the BIN — identify the bank that issued it.
  3. MevvPos looks the BIN up and sends the payment to your POS at that bank, if you have one. Otherwise it falls to your default POS.
  4. Instalments are offered only when the card belongs to that POS’s own bank, because banks do not grant instalments on another bank’s cards.
  5. The customer goes through 3-D Secure at the bank, and the bank returns to a single callback address on your site.
  6. The signature on the return is verified with the key of the POS the order actually used, the order is completed and the result is recorded.

The card number, expiry and CVV are never written to your database or to the WooCommerce session. The only card data kept is the first six digits and the recognised bank name.

With the international providers no card data reaches your site at all: the card is typed on the provider’s own page. That is what makes them the shortest route to a small PCI scope — and it is also why they carry no BIN routing and no instalments.

The international providers

The international providers cover the world outside Türkiye. Every one of them uses the provider’s own hosted or redirected page, so card details are never typed on your site. Authentication runs there too, but the outcome is not left there: MevvPos records whether the payer was authenticated, sends them to the bank when the issuer asks for a challenge, and reads the ECI to record whether chargeback liability shifted. Where a provider does not report the outcome, it is recorded as unknown rather than as yours. Beyond the card, local methods go out in each provider’s own field: iDEAL, Bancontact, SEPA Direct Debit, Klarna, EPS, Przelewy24, BLIK, TWINT, Multibanco and wallets. You pick which ones an account offers, and a method you did not pick is never sent. A method whose currency does not match the order is dropped outright — offering iDEAL against a dollar order would only make the payment fail at the provider.

Stripe
A Checkout Session in mode=payment, which creates a Payment Intent behind Stripe’s own page. The secret key goes in the merchant key and the Webhook signing secret in the extra field. The order is completed by a signed payment_intent.succeeded. Elements is deliberately not used: running it would move the card fields onto your page and enlarge your PCI scope.
PayPal
Orders v2, in three steps: create the order, the customer approves it at PayPal, and MevvPos captures it. The third step is the one that is easy to miss — an approved order that is never captured leaves the money uncollected while the order looks paid. The REST client ID goes in the merchant number, the client secret in the merchant key, and the Webhook ID in the extra field.
Mollie
Payments API v2. The API key — live_… or test_… — goes in the merchant key and there is no extra field, because Mollie does not sign its notifications. See the webhook section below for what MevvPos does instead.
Square
A Payment Link through the Checkout API. Enter the access token as the merchant key, the Location ID and the Webhook signature key. Square ties every sale to a location: without the Location ID no payment can be created at all, and Square’s own error does not say so plainly.
Authorize.Net
Accept Hosted. The API Login ID goes in the merchant number, the Transaction Key in the merchant key, and the Signature key in the extra field — a different value from the Transaction Key and easy to confuse with it. This is the one provider that does not hand back a redirect address: it returns a token that MevvPos posts to the hosted page as a form.
Checkout.com
The Hosted Payments Page. The secret key (sk_…) goes in the merchant key and the webhook signature key in the extra field; the notification is signed with HMAC-SHA256 over the raw body. One behaviour to plan for: a refund is accepted asynchronously. Checkout.com answers the refund call with 202 and an action id, which means the request was taken — not that the money has gone back yet.
Razorpay
A Payment Link, for stores selling into India, where the card on its own is not enough and UPI, netbanking and wallets are gathered on the provider’s page. The Key ID goes in the merchant number, the Key Secret in the merchant key, and the webhook secret — a separate value from the Key Secret — in the extra field. Razorpay hands back two identifiers and they are not interchangeable: the link id (plink_…) is what status is asked with, while a refund is only possible with the payment id (pay_…) that the link reports once it has been paid.
Paystack
A hosted transaction page, for stores selling into Nigeria, Ghana, South Africa, Kenya and Côte d’Ivoire, where USSD, bank transfer and mobile money matter as much as the card. The secret key (sk_live_… or sk_test_…) goes in the merchant key and there is no extra field — the notification is signed with HMAC-SHA512 using that same secret. Everything hangs off a single reference: starting the payment, asking its status and refunding it all use it.

Test mode swaps in the provider’s own sandbox address for PayPal, Square and Authorize.Net. Stripe and Mollie separate live from test by the key rather than by the address, so there the key you paste is what decides.

Amounts are carried as whole numbers in the currency’s minor unit from end to end — kuruş, cents — and never as a decimal fraction. Mollie is the one provider that wants a decimal string back, and it wants exactly the right number of digits for the currency: 10.0 is refused where 10.00 is accepted. That string is built from the integer, not from a float.

None of the eight has been verified end to end with a live merchant account. Each provider file says so in its own header, and lists the documentation pages it was written from. The flows follow the providers’ published documentation, and the signature algorithms and field names are pinned to it, but that is not the same thing as a completed live payment and we will not describe it as if it were. Read them as ready to connect and test against your own sandbox rather than as proven in production.

POS records

The POS bar sits above the tabs and is visible on every one of them. Each card shows the bank, the merchant number, and a badge when something needs your attention.

Default
The POS that takes the payment when no better match is found. There is always exactly one.
Inactive
Kept but not collecting. Use this rather than deleting when a POS is configured but not yet live at the bank — a customer holding that bank’s card would otherwise be unable to pay.
Waiting for a licence
The record exists but is dormant because the licence is not active. Nothing was deleted; it resumes where it left off when the licence is renewed.
API details have not been entered
The record has no merchant number yet.

Banks are shown by a colour stripe rather than a logo. Bank logos are trademarks, and fifteen of them is both legal exposure and permanent maintenance — you already know which bank is yours.

The last POS cannot be deleted and the last enabled one cannot be switched off. To stop taking card payments entirely, turn the payment method off on the General tab instead.

There is no cap on the number of provider accounts, and routing between them — by the card’s issuing bank and by the order’s currency — is part of the free plugin. Earlier releases allowed one account; that limit was removed in 4.9.0.

When a licence lapses, the account that keeps collecting is not picked at random: it is the one marked default, or else the first configured enabled account. Picking at random would mean a store quietly starts collecting through a different bank one morning.

Bank API — credentials and the gateway address

Provider
The list comes in two groups: Türkiye — bank virtual POS and International payment providers. Everything else the form asks you follows from this one choice, and settings that cannot apply to your choice are not shown at all.
Bank
Choose the bank your POS is with. The gateway address and the BIN list used for instalments both follow this choice. Institutions with no implementation yet are listed with “— yakında” and cannot be selected.
Merchant number (Client ID)
The merchant number the bank gave you.
Merchant key (store key)
The merchant security key. Stored as a password field; once saved it is shown masked.
Gate URL
The address the payment form posts to.
Send the order’s own currency to the bank
Turkish virtual POS only, and off by default: MevvPos sends Turkish lira, which is what your bank expects unless you hold a multi-currency agreement. The currency is part of the signature, so a value your bank does not accept is refused without an explanation. The international providers take the order’s currency as it is and are never asked this question.
Turn test mode on
Stops the automatic redirect so you can inspect the request before it is sent.

Leaving Store Key blank when you save means “do not change it”. Writing the blank value would wipe the key and your store would stop taking payments without a single error message. The same rule applies to the other secret fields.

Some families need extra credentials, and the form shows them only for the provider you picked:

  • Garanti BBVA — Merchant ID, Authorisation username, Authorisation password.
  • VakıfBank — Terminal No, and an MPI address (leave it empty to use the test address).
  • PayTR — Merchant Salt.
  • iyzico, Craftgate, Sipay — no extra fields: the API key goes in Merchant number and the secret in Merchant key.
  • Stripe — Webhook signing secret.
  • PayPal — Webhook ID.
  • Square — Location ID and Webhook signature key.
  • Authorize.Net — Signature key, used for webhooks only.
  • Mollie — no extra fields: the API key goes in Merchant key.

These extra credentials go into the signature. If one is missing, the signature is computed with an empty value and the bank refuses the payment silently — no error, no message, just a decline.

For the seven NestPay banks the gateway address is filled in from your bank choice, so you can leave Gate URL empty. For the others the field is not pre-filled: enter the address your bank or provider gave you, otherwise the payment form has nowhere to post.

The thirteen supported institutions

NestPay / Asseco (Payten)
İş Bankası, Akbank, Halkbank, QNB, Şekerbank, TEB, Ziraat Bankası
Garanti GT3D
Garanti BBVA
PayFlex V4
VakıfBank
Payment institutions
iyzico, PayTR, Craftgate, Sipay

Institutions without an implementation stay in the bank list on purpose, marked “— yakında”. Removing them would hide which protocol families are still missing. If yours is one of them, the Bank request tab is where you say so.

Only the NestPay signature has a golden-vector test against the bank’s own published example. Every other provider is marked beta in its own source: the flow is implemented to the documentation, but it has not yet been verified end to end with a live merchant account at that institution. That label stays until it has been.

Payment institutions do not issue cards, so they carry no BIN list. You select them as your POS rather than routing to them.

Webhooks — where the order is really completed

Every international provider needs one address on your site, and it has the same shape for all of them: ?mevvpos-webhook=<provider> on your home address. The exact value is printed read-only on the Bank API tab; clicking the field selects it.

What completes the order is the webhook, not the customer returning to your site. The return address can be guessed and a customer can reach it without having paid, so a browser arriving there proves nothing on its own. MevvPos never reads the result off the return: it either waits for the signed notification or asks the provider directly, server to server. Until one of those two answers arrives, the order stays pending and an order note says so.

  1. Copy the address from the Bank API tab.
  2. Mollie is the exception and needs nothing pasted anywhere: MevvPos sends the address with each payment it creates. It is left out when the site answers on a local address, because Mollie could not reach it anyway — during local development the result comes from the status query instead.
  3. Paste it into the provider’s own panel exactly as it appears. Square computes its signature over the notification address together with the body, so one character of difference — a trailing slash is enough — makes every event fail verification, silently.
  4. Save the signing secret the provider hands back into that provider’s extra field.
  5. Send a test event from the provider’s panel and check that an order actually moves.

Mollie does not sign its notification at all — the body carries nothing but a payment id. That looks unsafe and is in fact safer: MevvPos does not trust the notification, it treats it as a doorbell. It takes the id and asks Mollie directly what the payment’s status is, and only an answer of paid produces an event. An attacker can ring the bell with an invented id but cannot forge Mollie’s answer, so a forged notification can never mark an order paid. If Mollie cannot be reached, the answer is “I do not know” and nothing changes.

PayPal’s signature is not verified locally either. It is a certificate chain, and a silent mistake in verifying a chain makes the signature meaningless; PayPal publishes an endpoint for it, and anything other than SUCCESS means the event is ignored.

Three rules hold for every provider. The reply is always an empty 200 — a verified event, a duplicate and rubbish all get the same answer, because a different reply would tell an attacker which guess was closer and an error reply would only make the provider retry an event that can never verify. PayTR is the one exception, because it wants the literal text OK and repeats the notification for hours without it. The same event delivered twice has no second effect. And which account an event is verified against is read from your own configuration, never from the request — otherwise an attacker could nominate the key their own forgery is checked with.

The webhook does not go through WooCommerce’s endpoint. It has its own query variable and is caught before the theme, the page and the query machinery run at all — which is also why it works on a site with no WooCommerce installed.

BIN-based routing

A card’s first six digits identify the bank that issued it. MevvPos matches on exactly those six digits.

  • A snapshot of the BIN table ships inside the plugin — 1.449 BINs across 36 banks in the current build — so routing works offline, with no account of any kind, from the moment you install it.
  • Pro refreshes it weekly from our servers. The refresh merges over the packaged table rather than replacing it: a partial or empty response must never leave a store with no BIN data at all.
  • A response carrying fewer than a hundred BINs is rejected as implausible and not written.
  • Conflicts — the same BIN claimed by two banks — are resolved on our side before the list is sent. A store that resolved them locally could count a card as “ours” and route it elsewhere at the same time.

An unrecognised BIN is never refused. It falls through to your default POS. Turning away a card we simply do not have on file would be losing a sale to protect a lookup table.

The same table answers a second, different question at checkout: is this card from this POS’s own bank? That is what decides whether instalments are offered — see below.

BIN accuracy is not a paid feature. What Pro pays for is freshness, not correctness: the packaged list is the same list, just frozen at build time.

Instalments and commission

Rates are set per POS, from single charge up to twelve, on the Instalments and commission tab. They are percentages: write 5.50 for 5,5%.

This works on both checkout surfaces. On the block page the instalment table is drawn by the block itself and the surcharge is added to the cart as a fee, exactly as on the classic page, so choosing an instalment updates the total before the order is placed.

Instalments are a Turkish institution and the tab knows it. While an international provider is selected the rate grid is not shown at all, and the tab says so in a sentence instead: filling in twelve rows that can never apply would be a way of misleading you.

0
The option is shown and no commission is added.
Empty
The option is not shown at all. Empty and zero are not the same thing.

The commission appears in the cart as a taxable fee named “N Taksit Komisyonu”, calculated on the cart total including shipping and tax.

Instalments are offered only on cards issued by that POS’s own bank, and this is enforced on the server — not merely hidden in the interface. A card from another bank is forced back to a single charge and the fee is cleared.

The ceiling is twelve in every installation — that is the limit the banks themselves apply, not a product limit. It is enforced in three places: the list the customer sees, the value that arrives with the form, and the value the engine sends to the provider. A modified form cannot buy more instalments than you offer.

The settings form always shows all twelve fields, whatever your licence. If they disappeared when a licence lapsed, saving the page would quietly delete rates you had already entered. Fields above your ceiling are marked “Unlocked in Pro” and your numbers are kept.

There is no bank-supplied instalment table. The rates are the ones you enter, and commission on a past order is calculated from the rate that applied on the day of the sale — changing a rate today does not rewrite yesterday’s report.

The payment flow and card handling

Every Turkish family goes through 3-D Secure. There is no non-3D mode and no setting to turn it off. On the international side MevvPos speaks EMV 3-D Secure 2: it records whether the payer was authenticated with or without a challenge, sends the customer to the bank when the issuer asks for one, and reads the ECI to record whether chargeback liability shifted. Two things it deliberately does not do: it never claims an exemption a provider has no field for, and it never reports liability as yours when the provider does not tell us the outcome — “we do not know” and “the liability is the merchant’s” are different answers.

Form flow
NestPay, Garanti, PayTR and Sipay: the browser posts a signed form to the bank, the customer authenticates, and the bank returns to your site.
Server flow
VakıfBank, iyzico and Craftgate: your server talks to the provider, gets the 3-D page, shows it, and completes the sale server to server after authentication.
Redirect flow
Stripe, PayPal, Mollie, Square, Checkout.com, Razorpay and Paystack: MevvPos creates the payment over the provider’s REST API and sends the customer to the address the provider returns.
Token form flow
Authorize.Net: the provider returns a token instead of an address, and the token is posted to its hosted page as a form. Writing the token into a URL would leak it in browser history and server logs.

The Turkish banks always return to one address on your site: ?wc-api=mevvpos_callback. The signature on that return is verified using the key of the POS the order actually used, recorded on the order itself — with more than one POS, verifying against the wrong key produces a hash error on every single payment.

The card never reaches your database. It is held in the browser for the length of the redirect and removed afterwards. If it is not there when the pay page loads — a new tab, a refresh, storage disabled, a back-navigation from the bank — a visible card form is shown instead of sending empty fields to the bank. The card form is visible by default, on purpose: a payment flow cannot depend on JavaScript having run.

When the bank approves the payment, the order is completed even if the 3-D status code was unexpected — an order note records the anomaly and asks you to confirm it from the bank’s own screen. If the bank says approved, the customer has been charged; refusing that would mean “your card was charged but your order failed”.

Each attempt records the POS used, the card’s bank and BIN, the instalment count and rate, the result, the bank’s response code and message, and the authorisation and transaction references.

Pre-authorisation, payment links and risk rules

Authorise now, capture later

Each POS record carries a collection method: charge straight away, or hold the amount and collect it later. With the second, the money is reserved on the card at checkout and the order screen gains two buttons — capture (all of it, or part) and release. Capture it when the parcel goes out; release it when you cannot supply.

The setting only appears where the provider really carries it: the NestPay bank family (İşbank, Akbank, Halkbank, QNB, Şekerbank, TEB, Ziraat) plus Stripe, Authorize.Net and Checkout.com. For every other provider the choice is not offered at all, rather than offered and then failing at the gateway.

A payment link

The Payment links screen creates a link for an amount and, optionally, a description and an expiry. Whoever opens it lands on the store’s ordinary checkout with a pending order already made, which means the same BIN routing, the same instalment table and the same 3-D Secure step as any other payment. Nothing is a shortcut around the payment engine.

  • The token is 32 random bytes; the address cannot be guessed from another one.
  • The link expires on the date you set.
  • It works once. After the order is paid the link stops opening, so a forwarded message cannot be paid twice.

Risk rules

The Risk screen holds five rules, each switched on and given its own threshold separately. They are evaluated before the card is sent to any provider, and each one can flag the order, hold it for review or decline it outright.

  • Attempts per address — how many payment attempts one visitor may make in a window.
  • Attempts per card — the same count for one card.
  • Consecutive failures — a run of declines in a row.
  • Country mismatch — a Turkish-issued card with a billing address outside Türkiye.
  • Amount ceiling — an order total above which you want to look first.

No address and no card number is written down. What the counters hold is a digest salted with your own site key, so the rules can recognise a repeat without the screen ever becoming a second card database. There is no device fingerprint, no machine-learned score and no BIN block list — five rules you can read, and nothing you cannot explain to a customer.

Refunds

For the international providers, refunds are issued from the WooCommerce order screen in the usual way — full or partial. MevvPos sends the refund to the provider against the payment reference recorded on the order.

Refunds are never gated by the licence. Not being able to give a customer their money back is not something a renewal reminder is allowed to cause, and in most countries it is a legal obligation besides.

Turkish bank virtual POS cannot be refunded from WordPress yet, and MevvPos says so instead of pretending. Refund the transaction from your bank’s own screen. Silently reporting success would make WooCommerce mark the order refunded while the money sat at the bank — a wrong accounting record costs far more than a button that does not work.

A refund needs the payment reference the provider gave back. An order placed before that reference was recorded, or one whose provider account has since been deleted, cannot be refunded through the plugin.

Reports

The Reports tab shows revenue, successful transactions, success rate and the single-charge vs instalment split, with a daily chart. Transactions still waiting for the bank to return are counted separately and left out of the success rate.

The revenue summary covers 7, 30 or 90 days in every installation. The Pro add-on adds three breakdowns on top of it:

  • Instalment and commission load — count, revenue and commission burden per instalment tier.
  • POS and bank breakdown — revenue, successes, failures and success rate for each POS and for each issuing bank.
  • Decline code distribution — with CSV export. A repeating decline code points at something you can fix: insufficient funds is the customer’s problem, but 3-D verification and POS configuration errors are yours.

The data behind the reports is collected by the free plugin, so history keeps accruing whether or not you have Pro. It has to be that way: past data cannot be generated retroactively when you upgrade.

A freshly installed store has no chart. Recording begins with the plugin, and the screen fills after the first payment attempt.

Using the engine without WooCommerce

The WooCommerce gateway is now one consumer of the payment engine rather than the engine itself. The engine knows nothing about orders, carts or sessions: you hand it a payment intent — an amount as a whole number of minor units, a currency, a reference and the return addresses — and it hands back either a form to post or an address to redirect to. A form plugin, a booking plugin or your own code can drive it the same way the checkout does.

  • MevvPos_Payment_Engine::start() begins a payment and returns what the browser should do next.
  • MevvPos_Payment_Engine::handle_webhook() is the only path that changes a payment’s state. An event whose signature does not verify changes nothing and is recorded.
  • MevvPos_Payment_Engine::webhook_url() builds the address you paste into the provider’s panel.
  • The instalment ceiling is applied inside the engine as well, as a last line of defence — the calling screen may have been rendered before the licence lapsed.

If you registered your own provider through the mevvpos_providers filter before version 4.0.0, it now receives a payment intent instead of a WC_Order. Requests and signatures for the seven Turkish families are unchanged, so existing POS setups keep working untouched.

Bank requests and support

Bank request
Lists every institution that is not implemented yet, with the reason: either the protocol family has not been determined, or the family is known and we are waiting for documentation. You can open a request for one, or name an institution that is not in the list at all.
Support
A subject, the POS or bank concerned, what happens when you do what, and the error message the bank showed.

The support form asks you not to include a card number or security code. They are never needed to diagnose a payment problem, and no payment or card data is sent with either form.

The free plugin contacts our servers only when you press one of these buttons. Nothing is sent on a schedule and there is no licence call at all in the free edition.

Free and Pro

Nothing in the free plugin is locked, limited or counted. Every Turkish institution, every international provider, 3-D Secure, hosted-page checkout, wallets, multi-currency, currency and BIN routing, as many POS accounts as you have merchant agreements, instalments to twelve, test mode, refunds, pre-authorisation, payment links, the risk rules and the 7 / 30 / 90 day revenue report all work with no key and no account. Pro is a separate plugin whose code is not in the free package at all — it adds capabilities rather than raising a ceiling.

Free
Everything on this page unless it says otherwise: unlimited provider and POS accounts with BIN and currency routing, instalments to twelve, the 7 / 30 / 90 day revenue report, the packaged BIN table, pre-authorisation, payment links, risk rules, refunds and unlimited transaction volume. Apple Pay and Google Pay too, because they appear on the provider’s hosted page by themselves.
Pro
Recurring payments and subscription billing, the mandate store, stored payment authorisations in My Account, SCA exemption requests, the POS / bank / instalment / decline-code breakdowns with CSV export, and a weekly live BIN refresh. A separate plugin, downloaded from your account here — it is not on WordPress.org.

Multi-currency and routing by currency are both free. They were briefly sold with Pro; charging for the ability to point a euro order at the euro account was pricing a limitation rather than a capability, and it is gone.

When a licence expires or is absent:

  • Nothing in the free plugin changes. It does not ask about licences at all — the question was removed from that package in 4.9.1 — so every account, every instalment plan, every route and the whole revenue report keep working exactly as before.
  • Your store keeps taking payments. There is no licence check anywhere on the payment path. Cutting off a shop’s revenue is not an acceptable way to send a renewal reminder.
  • Refunds keep working, because they are not gated at all.
  • Recurring charges already scheduled stop being taken, and the subscription screens go quiet. Mandates and stored authorisations are never deleted; they resume on renewal.
  • The breakdowns and CSV export stop. The 7 / 30 / 90 day report underneath them is in the free plugin and stays. Collected history is not deleted.
  • The live BIN refresh stops; the packaged table keeps working.

If our servers are unreachable, an active licence keeps working for seven days on the strength of the last successful check. But a licence whose own expiry date has passed reads as expired regardless — otherwise blocking our address would be a way to extend a licence by a week.

Updates

The Pro plugin carries an updater tied to your licence. It has to: Pro is not published on WordPress.org, so there is nowhere for WordPress to learn that a new version exists. With an active licence a Pro update appears on the plugins screen like any other.

The free plugin now announces its own updates as well. It asks our server whether a newer version exists and hands the answer to WordPress’s own update flow, so a new release turns up on the plugins screen with an ordinary Update button instead of waiting for someone to fetch a zip by hand. The request carries only the package name and the installed version — no site address, no e-mail address, no licence key, no plugin inventory. Because nothing in it identifies you, trust rests entirely on the signature: the reply is signed with our Ed25519 key, and one that is unsigned, wrongly signed, replayed from an old timestamp or that points the download at any host but ours is discarded without touching the site. Your POS records, credentials and collected history live in WordPress options and order meta and an update never touches them.

When something does not work

The bank refuses every payment with no useful message
Almost always the signature. A wrong signature raises no error anywhere — the bank simply declines. Check the merchant number, the store key, and any extra credentials for that family: they all go into the signature. A useful test is that banks answer a signature error and an invalid card differently; getting an “invalid card” message means your signature is right.
“Hash error” on the way back, with more than one POS
The return is a separate request with no session. MevvPos records which POS an order used and verifies with that key. If you deleted the POS record an order was paid through, verification falls back to the default POS and can fail.
The customer pays at Stripe or PayPal but the order stays pending
The webhook address is not reachable or not registered. The order is completed by the notification, not by the customer’s return, so a missing webhook looks exactly like a customer who never paid. Check that the address on the Bank API tab is the one saved in the provider’s panel, and that the provider’s test event arrives.
Square events are all rejected
Square signs the notification address together with the body. The address registered at Square must match the one on the Bank API tab character for character — a trailing slash, http instead of https, or a www that one side has and the other does not is enough.
Authorize.Net events never verify
The Signature key is a separate value from the Transaction Key and the two are easy to swap. Webhooks are verified with the Signature key only.
The customer reaches a blank pay page, or the Öde button does nothing
A caching or optimisation plugin is deferring scripts. On LiteSpeed, exclude mevvpos, jquery and the WooCommerce frontend scripts from the delay list. The card form is shown by default so the flow still works, but the automatic redirect does not.
The order stays “pending” after a successful payment
The bank or provider did not reach the callback address. Check that ?wc-api=mevvpos_callback is reachable from outside — a maintenance mode, an IP restriction or a login wall in front of the site will block it.
The payment form posts back to the same page
The Gate URL field is empty for a bank whose address is not pre-filled. Enter the address your bank or provider gave you.
Test mode is on but the payment still goes to the live bank
Test mode stops the automatic redirect and shows you the request; it does not rewrite the gateway address for the NestPay banks. Put your bank’s test address into Gate URL while you are testing. On Stripe and Mollie the switch does not change the address either — there the test key is what selects the sandbox.
Instalments do not appear
Either the rate for that count is empty rather than zero, the card is from another bank, or the selected provider is an international one and has no instalments at all.
Cards go to the wrong POS after an upgrade
Older versions carried hand-written BIN ranges that were partly wrong. Check that each POS is filed under the bank it is really with.
The admin screen looks unstyled, or a fix does not appear
A stale browser cache. Reload the page bypassing the cache.

Limits

The list below is deliberate. None of it is a bug.

  • No refunds for Turkish bank virtual POS. No Turkish family carries a refund call; refund from your bank’s own screen. The international providers do, and WooCommerce shows the refund button for them.
  • The updater depends on where you got the plugin. The build downloaded from mevvsoft.com updates itself from our servers. The build published on WordPress.org does not carry that code at all — a plugin hosted there must be updated through WordPress.org, so the two packages differ in exactly this one file.
  • Recurring payments work with the international providers only, and there is no saved-card picker. A stored collection permission is created, renewed, retried and marked past due, and a WooCommerce Subscriptions product sets one up without any code. What is missing: the Turkish bank virtual POS families cannot carry one, a shopper cannot choose a saved card at checkout, and there is no dunning schedule or cancel-from-admin screen.
  • Pre-authorisation exists, but not everywhere. A POS can hold the amount and capture it later — in full or in part — from the order screen, or release it. Only the NestPay bank family, Stripe, Authorize.Net and Checkout.com carry it; every other provider is a direct sale and the setting is not shown for them.
  • No rule-based routing. Routing is by currency and issuing bank only — not by amount, card brand or country.
  • Routing asks the currency first and the issuing bank second, in every installation. What is still missing is everything past that: no failover to a second account, no cascading retry, and no routing by amount, card brand or country.
  • Both checkouts are supported. The classic payment form and the WooCommerce Checkout block are both served, and the instalment table and its surcharge behave the same way on either. The block writes the chosen BIN and instalment through the Store API, so the surcharge is calculated by the same code as on the classic page.
  • 3D Pay Hosting was rejected on purpose. The hosted page removes PCI scope but also removes the BIN and the instalment table — and the entire value of the Türkiye side is in the routing they make possible. The international providers are hosted precisely because there is nothing to route there.
  • No BIN editor. The table is managed by us and merged with the live refresh; there is no screen for editing it by hand.
  • Twenty-six institutions are listed but not implemented. They stay visible so the gap is visible.
  • Every provider except NestPay is self-declared beta until it has been verified end to end with a live merchant account.
  • The tabs are in-page. The sidebar entries open the MevvPos screen; switch tabs on the page itself.

What is never stored, in any form: the card number, the expiry date and the security code. On the Türkiye side the only card data kept is the first six digits and the bank they identify; with the international providers not even that, because the card is never typed on your site. Storing the security code is forbidden under every circumstance, and even a masked one leaks its length.