Direct API Payments
The Direct API lets you collect card details on your own form and submit them to Flowlix for processing. This gives you full control over the payment experience but requires your systems to handle raw card data.How it works
- The customer enters their card details on your checkout page.
- Your server sends a
POST /v1/paymentsrequest with the card details, amount, and currency. - Flowlix returns a
201 Createdresponse immediately. The payment is inpendingstate and is being processed asynchronously. - Your server polls
GET /v1/payments/{id}until the payment reaches a terminal status:succeeded,failed,expired, or — if 3D Secure is required —requires_action(in which case you redirect the customer tonext_action.redirect_urland then resume polling).
Create a payment
Send aPOST request to /v1/payments with the payment details:
Request parameters
| Parameter | Required | Description |
|---|---|---|
amount | Yes | Amount in minor units (e.g. 4999 = EUR 49.99). |
currency | Yes | Three-letter ISO 4217 code: eur, usd, or gbp. |
card | Yes | Card details object (see below). |
card.number | Yes | The full card number (digits only, no spaces). |
card.exp_month | Yes | Expiration month (1-12). |
card.exp_year | Yes | Expiration year (4 digits). |
card.cvc | Yes | Security code (3 digits for Visa/MC, 4 for Amex). |
card.holder_name | Yes | Cardholder name as printed on the card. |
customer | No | Customer details object. Optional, but if you supply it, every sub-field below is required. |
customer.email | Conditional | Customer email for receipts and fraud checks. |
customer.first_name | Conditional | Customer first name. |
customer.last_name | Conditional | Customer last name. |
customer.country | Conditional | Country code (ISO 3166-1 alpha-2, e.g. DE). |
customer.phone | Conditional | Phone in international format (e.g. +491701234567). |
customer.address | Conditional | Street address. |
customer.city | Conditional | City. |
customer.zip | Conditional | Postal code. |
customer.state | No | State or province (required by some upstream networks for US/CA). |
customer.ip | No | Customer IP address. If omitted, Flowlix uses the request remoteAddr — pass the real client IP explicitly when you sit behind a proxy or CDN. |
return_url | No | URL the customer is redirected to after a 3D Secure challenge. Required if the card triggers 3DS. Must use https (or http://localhost for local development). |
description | No | Free-text description for your records. Maximum 500 characters. |
metadata | No | Key-value pairs for custom data. Maximum 50 keys. |
Create response
The create call returns201 Created immediately. The card brand and expiry
have not been resolved yet — those fields are populated by the upstream
processor and become available on the next GET /v1/payments/{id}:
HTTP
201 does not mean the card was charged successfully — it only
means Flowlix accepted the payment for processing. The terminal outcome
is reported via the status field on subsequent GET calls.Possible payment statuses
Inspectstatus on every retrieval and branch on it:
| Status | Meaning | Next action |
|---|---|---|
pending | Still processing. | Keep polling. |
requires_action | Customer must complete 3D Secure. | Redirect to next_action.redirect_url, then resume polling after the customer returns to your return_url. |
succeeded | Card was charged successfully. | Fulfill the order. |
failed | Card was declined or upstream processing failed. | Read decline_code / decline_message and show the customer an appropriate message. |
expired | Customer did not complete a required action (e.g. 3DS) in time. | Treat as failed. The payment cannot be reused — create a new one if the customer wants to retry. |
refunded | The payment was previously fully refunded. | No action; this is a terminal state. |
Handle declines
When a card is declined, the call still returns201 Created — the
declined outcome shows up on the Payment object retrieved via
GET /v1/payments/{id} once processing completes. The status becomes
failed and the decline reason is reported on the Payment itself, not as
an HTTP error envelope:
decline_code to show an appropriate message to the customer:
| Decline code | Customer message |
|---|---|
insufficient_funds | ”Your card has insufficient funds. Please try a different card.” |
expired_card | ”Your card has expired. Please use a different card.” |
do_not_honor | ”Your card was declined. Please contact your bank or try a different card.” |
generic_decline | ”Your card was declined. Please try a different card.” |
Full integration example
Here is a complete example in Python that creates a payment and polls until it reaches a terminal state:Best practices
- Always include an
Idempotency-Keyto prevent duplicate charges on retries. Use a random UUID per attempt unless you have a deterministic key tied to the order. - Treat
201as “accepted, in progress” — never as “succeeded”. Always poll for the terminal status. - Always pass
return_urlif there is any chance the card may trigger 3D Secure. Without it, 3DS-required cards cannot complete authentication. - Validate card input client-side before submitting (check Luhn, expiry, CVC length) to reduce unnecessary API calls.
- Store
payment.idin your database linked to the order for future retrieval and refunds. - Handle all decline codes, plus
expiredand validation errors. Don’t assume the only failure mode isfailed. - Never log full card numbers — log only the
last4from the response. - Pass
customer.ipexplicitly when your service sits behind a proxy or CDN; otherwise the upstream fraud engine sees the proxy IP. - Use metadata to link payments to your internal records (order IDs, customer IDs, etc.).
