A redirect to the bank is not proof of payment. A reliable integration separates the customer experience, server-side order creation and the provider’s technical confirmation.

Prepare the contract and URLs

Merchant ID, signing key, test environment and return URLs must match the CMI setup. The website needs public HTTPS to receive the server notification. Keep keys out of source code, screenshots and Git repositories.

Create orders on the server

The browser must not choose the amount, currency or order ID. The server reads the catalog, creates a unique identifier, stores the amount and signs only the fields expected by the gateway. This prevents visitors from changing the price before redirection.

Trust the signed notification

The server-to-server notification verifies signature, order ID, amount, currency and return code. A success page only displays the recorded state. Repeated notifications must be acknowledged without creating another order or requesting a second capture.

Test failures as carefully as success

Check declined cards, closed browsers, late notifications, altered amounts and invalid signatures. An event log should let you reconstruct the journey. Customer and internal emails are sent after confirmation, never before.

Understand the three actors in the journey

The customer sees your website, then the hosted CMI payment page. Your server remains responsible for the order: it calculates the amount, creates the identifier, stores context and signs the transmitted fields. CMI handles the card, bank authentication and authorization. Finally, the server callback tells your website what happened. This separation prevents a browser-modified form from deciding the price or payment status.

The browser return page improves the experience; it is not proof of payment. It can display pending, confirmed or failed according to what your server has already recorded. If the customer closes the browser after paying, the server notification must still update the order.

Prepare merchant values methodically

Client ID, signing key, environment, currency and URLs must be consistent. The signing key must never appear in HTML, Git, chat or public_html. Keep it in a private file outside the web root or in hosting environment variables. Success, failure and callback URLs need public HTTPS and must match the deployed domain exactly.

Choose your capture policy as well. Returning ACTION=POSTAUTH can request automatic capture after authorization. A simple approval keeps authorization pending for a manual action. This choice has commercial and accounting consequences; decide it before testing, not after a real order.

Build a locked server-side order

The stored order should contain the selected plan or product, calculated amount, currency, language, validated billing details, a unique identifier and the rnd value. Fields sent to CMI are built from this record, never from an amount freely supplied by the browser. When the callback returns, the server compares signature, merchant ID, currency, amount, order ID and the original rnd.

This locking also prevents duplicates. If the same notification arrives twice, the order already exists and the server can acknowledge it without requesting another capture. The order state should keep an event history: creation, browser return, authorization, failure, requested capture and sent emails.

Verify the signature before any business decision

Signature validation comes before interpreting the result. Excluded fields, sorting order, escaping rules and encoding must follow the CMI documentation exactly. An invalid signature ends the request; the system should never “guess” the order from browser data or a partial identifier.

After signature verification, ProcReturnCode=00 indicates a successful authorization. Other codes remain failures or unconfirmed states even if a browser-return page looks positive. Details such as TransId, HostRefNum, masked card and card brand can be stored for reconciliation without retaining sensitive card data.

Test the situations that actually happen

Testing is not limited to a successful card. Cover successful authorization, decline, browser interruption, return before notification, repeated notification, altered amount, unknown order and forged signature. Confirm emails are sent only after verified authorization and that visitors see a clear pending message when confirmation has not arrived.

Also validate hosting: available PHP, writable private directory, mail() or SMTP delivery, error logs, timezone and HTTPS. On shared hosting, an .htaccess rule, proxy or inaccessible folder can prevent the callback from reaching the application.

Production checklist

  • Final HTTPS domain in place with clean redirects.
  • Test CMI credentials configured in a private file.
  • One successful and one declined real test transaction.
  • Public-domain callback received and signature verified.
  • Order storage outside public_html, without card data.
  • Customer and administrator emails delivered.
  • Capture mode selected and documented.
  • Refund, dispute and support procedures defined.
  • Production credentials kept separate from test credentials.

After these points are validated, change only the environment values: merchant ID, signing key and production gateway URL. Never leave a test configuration active on a checkout collecting real payments.

Questions to ask before activation

Confirm the merchant ID, environment, signing key, allowed URLs, currency, 3D Pay Hosting method and capture policy. Ask how to review transactions, process a refund and contact support. Also verify whether IPs or domains must be registered for callback delivery.

A reasonable action plan

Deploy on an HTTPS domain first, configure private keys, then run one successful and one declined test transaction. Inspect the stored order, events and emails. Document the production switch process and keep useful logs without exposing the key or card data.

Security checklist

  • Signing key outside code, Git and the public web root.
  • Server-created order and amount, never supplied by the browser.
  • Signature, currency, client, amount, order and rnd verified in the callback.
  • Repeated notifications acknowledged without duplicate capture or email.
  • Event log usable by support and engineering.

The sign that the integration is ready

The integration is ready when success, failure, interruption and duplication have been observed on the real domain. A simple form redirecting to CMI is not enough. The order must exist before departure, authorization must be verified after the provider response and the user must receive an honest status.

Common mistakes to avoid

Do not place the store key in JavaScript, a public repository or a file under public_html. Do not treat the success URL as payment proof, accept an order ID that was not created by your server or mark a payment captured before the callback is verified. Avoid logging full card data or sending a “payment received” email before the stored order has been updated. The safest implementation is boring: strict fields, signature first, idempotent order update, clear event history and a tested production switch.