Merchant Setup Guide
This guide walks you through connecting your website to the Dapit Instant Payments platform, testing in the staging environment, and going live with real EUR payments.
Prerequisites
Before you begin, make sure you have:
- Your API key — provided during merchant onboarding. It looks like a long hex string (e.g.,
a1b2c3d4e5f6...). If you don't have one, ask your ISO admin to generate it from the MaxaFi platform. - A server-side language — PHP, Node.js, Python, Ruby, or any language that can make HTTPS POST requests. All API calls must come from your server, never from the browser.
- A webhook endpoint (recommended) — A URL on your server that can receive POST requests. We'll send payment confirmations here.
Step 1 — Verify Your API Key
Open a terminal and run this curl command (replace YOUR_API_KEY with your actual key):
curl -X POST "https://www.maxafi.com/api/gateway/gateway.php?action=create_session" \
-H "Content-Type: application/json" \
-H "X-Gateway-Key: YOUR_API_KEY" \
-d '{
"amount": 1.00,
"currency": "EUR",
"items": [{"name": "Test Item", "qty": 1, "price": 1.00, "total": 1.00}]
}'
Expected response:
{
"success": true,
"session_id": 1,
"session_token": "a1b2c3d4e5f6...",
"checkout_url": "/checkout/pay.html?session=a1b2c3d4...",
"environment": "stage"
}
"success": true and "environment": "stage", your API key is working and you're connected to the staging environment. Save the session_token — you'll use it in the next steps.
"error": "Missing API key" or a 401 error, double-check your API key. Contact your ISO admin if the key was revoked or expired.
Step 2 — Choose Your Integration Method
| Option A: API Integration | Option B: Hosted Checkout | |
|---|---|---|
| Developer needed? | Yes | No |
| Customer experience | Stays on your website | Redirected to Dapit-branded page |
| Branding | Fully your own design | Dapit page with your merchant name |
| Setup time | 1–2 hours | 10 minutes |
| Best for | Custom checkout, full control | Quick start, no-code merchants |
Option B — Hosted Checkout (no code)
If you don't have a developer, you can skip Steps 3–5 entirely. Just create a session from your server and redirect the customer:
// Create session (from your server), then redirect customer to: https://www.maxafi.com/checkout/pay.html?session=SESSION_TOKEN // We handle the entire payment UI. // When confirmed, we POST to your callback_url.
Then skip ahead to Step 6 (Webhook).
Option A — API Integration
Continue to Step 3 below. You'll build the payment flow into your existing checkout page.
Step 3 — Create a Checkout Session
When a customer clicks "Pay with Instant Payments" on your checkout page, your server makes two API calls:
Call 1: Create Session
// POST to create_session — creates the payment session const session = await fetch('https://www.maxafi.com/api/gateway/gateway.php?action=create_session', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Gateway-Key': 'YOUR_API_KEY' }, body: JSON.stringify({ amount: 49.99, currency: 'EUR', order_id: 'ORD-12345', // YOUR order number — returned in webhook customer_email: 'customer@example.com', customer_name: 'John Smith', callback_url: 'https://yoursite.com/webhooks/dapit', items: [ { name: 'Premium Plan', qty: 1, price: 49.99, total: 49.99 } ] }) }).then(r => r.json()); // Save session.session_token — you need it for all subsequent calls
Call 2: Create Invoice
This generates the reference number and returns the IBAN + beneficiary details:
// POST to create_invoice — generates reference number + payment instructions const invoice = await fetch('https://www.maxafi.com/api/gateway/gateway.php?action=create_invoice', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ token: session.session_token }) }).then(r => r.json()); // invoice.beneficiary_name → "Longemalle Trustees OU" // invoice.iban → "LT913740020058585210" // invoice.bank_name → "ConnectPay" // invoice.reference_number → "DP-260406-XA6HSX" // invoice.amount → 49.99
Send these values back to your frontend to display on the checkout page.
Step 4 — Display Payment Instructions
On your checkout page, display the payment details from Step 3. The customer needs to see:
| Field | Example Value | What to tell the customer |
|---|---|---|
| Beneficiary Name | Longemalle Trustees OU | "Send payment to this name" |
| IBAN | LT913740020058585210 | "Use this IBAN in your banking app" |
| Reference Number | DP-260406-XA6HSX | "Enter this EXACTLY as the payment reference" |
| Amount | €49.99 | "Send this exact amount" |
| Bank | ConnectPay | Optional — helps customer identify the bank |
The customer then:
- Opens their banking app (Wise, Revolut, N26, or any EUR-enabled bank)
- Creates a new SEPA transfer to the beneficiary name and IBAN
- Enters the reference number in the "Reference" or "Message to recipient" field
- Sends the exact EUR amount
- Returns to your page and clicks "I've Sent the Payment"
Add a prominent "I've Sent the Payment" button on your page. When they click it, you move to Step 5.
Step 5 — Handle "I Paid" and Poll for Confirmation
When the customer clicks "I've Sent the Payment", your page makes two types of calls:
5a. Tell our API the customer says they paid
// Call confirm_paid — tells our system to start checking ConnectPay await fetch('https://www.maxafi.com/api/gateway/gateway.php?action=confirm_paid', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ token: sessionToken }) });
5b. Poll every 7 seconds until payment is found
// Poll check_payment every 7 seconds const poll = setInterval(async () => { const check = await fetch('https://www.maxafi.com/api/gateway/gateway.php?action=check_payment', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ token: sessionToken }) }).then(r => r.json()); if (check.status === 'confirmed') { clearInterval(poll); // ✅ Payment confirmed! Show success page. } else if (check.status === 'partial') { clearInterval(poll); // ⚠ Customer sent less than the invoice amount. // Show: "We received €X. Remaining: €Y. Send the rest with the same reference." // When they send more: call confirm_paid again, restart polling. } else if (check.status === 'overpaid') { clearInterval(poll); // 💰 Customer sent too much. Order is confirmed. // Refund of (overpaid - €1 fee) will be processed automatically. } else if (check.status === 'timeout') { clearInterval(poll); // ⏰ Payment not found after ~7 minutes. Show retry or contact support. } // "pending" — keep polling, show a spinner }, 7000);
Step 6 — Receive Webhook Confirmation
When a payment is confirmed, we send a POST request to the callback_url you provided when creating the session. This is your server-side confirmation — use it to update your order database.
// Your webhook endpoint receives this JSON: { "event": "payment.confirmed", "merchant_order_id": "ORD-12345", // YOUR order number "reference_number": "DP-260406-XA6HSX", "invoice_amount": 49.99, "amount_paid": 49.99, "currency": "EUR", "paid_at": "2026-04-06 14:32:10", "customer_email": "customer@example.com", "customer_name": "John Smith" }
Return HTTP 200 to acknowledge receipt. If we don't get a 200, the notification is flagged as undelivered.
PHP Example
<?php $payload = json_decode(file_get_contents('php://input'), true); if ($payload['event'] === 'payment.confirmed') { $orderId = $payload['merchant_order_id']; $amount = $payload['amount_paid']; // Mark order as paid in your database $db->query("UPDATE orders SET status='paid' WHERE order_id='$orderId'"); // Send confirmation email, trigger shipping, etc. } http_response_code(200); echo json_encode(['received' => true]);
Test Accounts for Staging
Use these pre-funded test IBANs to simulate payments in the staging environment. No real money is involved.
| Account Name | IBAN | BIC | Currency | Balance |
|---|---|---|---|---|
| Bank Has No Money | LT923740020074597273 |
CNPALT21XXX | EUR | €100.00 |
| Bank Has Money | LT223740020032524104 |
CNPALT21XXX | EUR | €10,000.00 |
Run a Test Payment
Follow this exact sequence to test the full payment flow end-to-end:
- Create a session — call
create_sessionwith amount1.00and your webhook URL - Create an invoice — call
create_invoicewith the session token. Save the IBAN, beneficiary, and reference number. - Send a test payment — from one of the test accounts above, send €1.00 to the staging custodial IBAN with the reference number as the payment reference
- Confirm paid — call
confirm_paidwith the session token - Poll for confirmation — call
check_paymentevery 7 seconds. Within 30 seconds, you should getstatus: "confirmed" - Check your webhook — verify your endpoint received the
payment.confirmedPOST with yourmerchant_order_id - Verify via API — call
payment_statuswith the reference number to confirm it showsconfirmed
Integration Checklist
Confirm each of these before requesting production access:
create_sessionreturnssuccess: trueandenvironment: "stage"create_invoicereturns IBAN, beneficiary name, and a unique reference number- Your checkout page displays the payment instructions clearly
- The reference number is easy to copy (not buried in small text)
- After a test payment,
check_paymentreturnsstatus: "confirmed" - Your webhook endpoint receives and processes
payment.confirmed - Your webhook returns HTTP 200
payment_statusreturnsconfirmedwhen queried by referencelist_transactionsshows the completed test transaction- Your order database is updated when the webhook fires
Switch to Production
Once your staging integration tests pass:
- Contact your ISO admin — ask them to switch your merchant account from
stagingtoproductionin MaxaFi - No code changes needed — same API key, same endpoints, same flow. The only difference is payments use real EUR transfers.
- Verify the switch — call
create_sessionand check that the response includes"environment": "production" - Run one live test — create a €1.00 session and send a real €1 payment from your own bank account to confirm end-to-end
- You're live! — start accepting real payments from your customers
Payment Statuses
| Status | Meaning | What to do |
|---|---|---|
pending | Session created, waiting for customer | Show checkout page |
invoiced | Invoice + reference generated | Display payment instructions |
polling | Checking ConnectPay for the transfer | Show spinner, keep polling |
partial | Received less than the invoice amount | Show receipt + remaining balance |
confirmed | Full payment received ✓ | Show success, fulfill order |
overpaid | Received more than invoice amount | Order confirmed, refund auto-processed |
failed | Not found after max polling attempts | Show retry / contact support |
expired | Session timed out before payment | Create a new session |
Common Errors
| Error | Cause | Fix |
|---|---|---|
Missing API key (401) | No X-Gateway-Key header | Add the header to every API call |
Invalid API key (401) | Key doesn't match any active merchant | Check for typos. Ask your admin for a new key if needed. |
API key expired (401) | Key has a set expiration date | Generate a new key from the MaxaFi Info tab |
Session not found (404) | Invalid or expired session token | Create a new session. Tokens expire after 60 min by default. |
Session expired | Customer took too long | Create a new session and restart checkout |
Amount must be positive (400) | Amount is 0 or negative | Pass a positive number for the amount |
timeout status on check_payment | Payment not detected after ~7 min | Customer may not have sent it, or used wrong reference. Let them retry. |
Frequently Asked Questions
How fast do payments confirm?
SEPA Instant transfers arrive in 5–20 seconds. From the moment the customer sends the payment in their banking app, you'll typically see confirmed within 30 seconds of polling.
What if the customer doesn't include the reference number?
The payment won't be automatically matched. The session will eventually time out. The funds will still arrive in the custodial account — contact support to manually reconcile.
Can customers pay from any bank?
Yes — any bank that supports SEPA transfers to an IBAN. This includes Wise, Revolut, N26, traditional banks, and any EUR-enabled account worldwide. The customer doesn't need a European bank — they just need to be able to send EUR to an IBAN.
Are there chargebacks?
No. SEPA Credit Transfers are irrevocable once sent. There is no chargeback mechanism. This is the primary advantage over card payments.
What about refunds?
You can initiate refunds via the API (create_refund) or from the MaxaFi dashboard. Refunds are sent as outbound SEPA payments to the customer's IBAN. A €1 processing fee applies per refund. See the API Reference for details.
Do I need to change anything when switching from staging to production?
No. Same API key, same endpoints, same code. Your ISO admin flips a switch on your merchant account. The only visible change is that create_session will return "environment": "production" instead of "stage".
Where can I see my transactions and settlements?
Log in to MaxaFi, open your merchant account, and go to the Info tab → Instant Payments Gateway section. You'll see a full dashboard with transactions, refunds, and settlement reports.
I need help. Who do I contact?
Contact your ISO admin or reach out through the MaxaFi ticket system. For API issues, include your merchant name, the API endpoint you're calling, and the full error response.