FluxPay Developer Docs

FluxPay lets you accept online payments with one API. You create a payment, the customer pays, and we tell your server the result with a webhook.

Hosted payment page

Send the customer to a payment link. You never handle card data.

Direct card charges

Send card data from your own form. Needs PCI DSS compliance.

Automatic retries

If a card is declined, we can try another route for you.

REST API and SDK

Plain HTTP with JSON, plus a small JavaScript SDK.

Quick Start

  1. 1Get a test API key (sk_test_...). See API Keys.
  2. 2Create a payment on your server with POST /api/v1/payments.
  3. 3Redirect the customer to the paymentUrl in the response.
  4. 4Wait for the payment.success or payment.failed webhook, then fulfil the order.
cURL
curl -X POST https://fluxpay.online/api/v1/payments \
  -H "Authorization: Bearer sk_test_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 99.99,
    "currency": "USD",
    "description": "Order #1234",
    "customer": {
      "email": "customer@example.com",
      "firstName": "John",
      "lastName": "Doe",
      "phone": "+12125551234",
      "ip": "203.0.113.42",
      "billing": {
        "address": "123 Main Street",
        "city": "New York",
        "state": "NY",
        "country": "US",
        "postalCode": "10001"
      }
    },
    "metadata": { "orderId": "ORD-1234" },
    "successUrl": "https://yoursite.com/payment/success",
    "failureUrl": "https://yoursite.com/payment/failure"
  }'

Choose an Integration

There are two ways to take a card payment. Which one fits depends on your account setup.

Hosted payment linkDirect card charge
EndpointPOST /api/v1/paymentsPOST /api/v1/payments/charge
Who collects the cardThe payment pageYour own form
PCI DSSSmaller scopeFull compliance required
Works for every accountYesOnly when checkoutMode is DIRECT
Check checkoutMode before you collect a card
Some accounts route card payments to a hosted payment page. On those accounts the customer always types the card on that page. If you also collect the card on your own form, the customer types it twice. Call GET /api/v1/config?country=XX first:
  • checkoutMode: "DIRECT": you may collect the card and use the direct card charge.
  • checkoutMode: "HOSTED": do not collect the card. Create a payment link and redirect the customer.
If you are not sure, use the hosted payment link. It works for every account.

API Keys

Send your key in the Authorization header on every request: Authorization: Bearer sk_live_...

KeyModeWhat happens
sk_test_...TestNo real money moves. See Test Mode below.
sk_live_...LiveReal payments. Your account must be fully onboarded.

Get a key

  1. 1API access is switched on by the FluxPay team. Ask your account manager if you do not see it.
  2. 2Sign in to your dashboard and pick Test or Live mode with the switch in the header.
  3. 3Go to Settings > API Keys and click Generate API Key. Use Rotate later to replace it.
Keep keys safe
  • Use keys on your server only. Never put them in browser code, mobile apps or public repos.
  • Store them in environment variables, for example FLUXPAY_API_KEY=sk_live_...
  • Rotate a key right away if it leaks.

Test Mode

With a sk_test_ key no real money moves, and test payments do not count as revenue.

EndpointWhat happens in test mode
POST /api/v1/paymentsThe payment is marked COMPLETED straight away (status 201, testMode: true) and a payment.success webhook is sent. No card form is shown.
POST /api/v1/payments/chargeThe last 4 digits of the card number decide the result. See the table below.

Test cards (direct card charge)

Use any future expiry date and any 3-digit CVV.

Card number ends inExampleResult
1111, 1234, 99994111 1111 1111 1111Declined
2222, 2233, 22444111 1111 1111 22223DS check, then you choose the outcome
3333, 3344, 33554111 1111 1111 3333Stays PENDING
Anything else4242 4242 4242 4242Completed

Testing 3DS

  1. 1Charge a card ending in 2222.
  2. 2The response has action: "redirect" and a redirectUrl.
  3. 3Open the redirectUrl. It shows a 3DS simulator.
  4. 4Click Authenticate (Success) or Cancel (Fail).
  5. 5The customer goes back to your returnUrl and a webhook is sent with the result.

JavaScript SDK

The SDK is a thin wrapper around the REST API. Download the file that fits your project. You can skip the SDK and call the API directly.

FileUse it for
/sdk/v1/fluxpay.cjs.jsNode.js with require()
/sdk/v1/fluxpay.esm.mjsNode.js with import
/sdk/v1/fluxpay.min.jsScript tag (window.FluxPay)
Node.js
// curl -o fluxpay.cjs.js https://fluxpay.online/sdk/v1/fluxpay.cjs.js
const { FluxPayClient } = require('./fluxpay.cjs.js');

const fluxpay = new FluxPayClient({
  apiKey: process.env.FLUXPAY_API_KEY, // sk_test_... or sk_live_...
});

Script tag. The integrity value proves the file was not changed. Get current values from GET https://fluxpay.online/api/v1/sdk/integrity.

HTML
<script src="https://fluxpay.online/sdk/v1/fluxpay.min.js"
        integrity="sha384-..."
        crossorigin="anonymous"></script>

Using the SDK from a Website

Important: API keys must never appear in client-side code. Create payments on your server and return the paymentUrl to the browser.
Your server (Express)
app.post('/api/create-payment', async (req, res) => {
  const { payment } = await fluxpay.createPayment({
    amount: req.body.amount,
    currency: 'USD',
    customer: req.body.customer,
    description: req.body.description,
  });
  res.json({ paymentUrl: payment.paymentUrl });
});
Your page (HTML)
<button id="pay-btn">Pay $99.99</button>
<script>
  document.getElementById('pay-btn').addEventListener('click', async () => {
    const res = await fetch('/api/create-payment', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        amount: 99.99,
        customer: { email: 'customer@example.com' },
        description: 'Order #1234',
      }),
    });
    const { paymentUrl } = await res.json();
    window.location.href = paymentUrl;
  });
</script>

SDK Methods

MethodCalls
createPayment(body)POST /api/v1/payments
chargePayment(body)POST /api/v1/payments/charge
getPayment(id)GET /api/v1/payments?id=...
Node.js
const { FluxPayClient, FluxPayError } = require('./fluxpay.cjs.js');
const fluxpay = new FluxPayClient({ apiKey: process.env.FLUXPAY_API_KEY });

try {
  const { payment } = await fluxpay.createPayment({
    amount: 49.0,
    currency: 'EUR',
    customer: { email: 'customer@example.com', firstName: 'Jane', lastName: 'Smith' },
    successUrl: 'https://yoursite.com/payment/success',
    failureUrl: 'https://yoursite.com/payment/failure',
  });
  console.log(payment.paymentUrl);

  const { payment: latest } = await fluxpay.getPayment(payment.id);
  console.log(latest.status);
} catch (err) {
  if (err instanceof FluxPayError) console.error(err.statusCode, err.message);
}
TypeScript
The downloadable files are plain JavaScript and do not include type definitions.

Create a Payment

POST /api/v1/payments creates a payment and returns a paymentUrl. Send the customer to that URL.

Live mode needs a fully onboarded account
In live mode the request returns 403 { "error": "merchant_not_active" } while your account is still being onboarded or is suspended. Test mode always works.

Parameters

amountnumberrequired
Amount, for example 99.99. Must be more than 0. Currencies without decimals (JPY, KRW and others) must be whole numbers.
customer.emailstringrequired
Customer email.
currencystring
3-letter code. Default USD.
customer.firstName / lastNamestring
Recommended. Pre-fills the payment page.
customer.phonestring
Pre-fills the phone field.
customer.ipstring
Customer IP address, used for fraud checks.
customer.billingobject
address, city, state, postalCode, country (2 letters, for example DE). Send country whenever you can: it decides which routes can take the payment. Without it we use customer.country, then your business country.
descriptionstring
Shown to you and the customer.
metadataobject
Your own data, for example an order ID. Sent back in webhooks.
successUrl / failureUrl / returnUrlstring
Where the customer goes after paying. See Redirect URLs.
webhookUrlstring
Webhook URL for this payment only. Overrides the one in your dashboard.
languagestring
Payment page language: en (default) or fr.
autoRedirectboolean
true skips our payment page when your account uses a hosted payment page. Needs customer.firstName, lastName, email and billing.country. See Skip the payment form.
affiliateTagstring
Links the payment to an affiliate. See Affiliate Tracking.
threeDSstring
auto (default), always or never. Only if the FluxPay team switched this on for your account. Otherwise any value other than auto returns 403.
SDK
const { payment } = await fluxpay.createPayment({
  amount: 99.99,
  currency: 'USD',
  customer: {
    email: 'customer@example.com',
    firstName: 'John',
    lastName: 'Doe',
    billing: { country: 'US' },
  },
  description: 'Order #1234',
  metadata: { orderId: 'ORD-1234' },
});

// Send payment.paymentUrl to the browser and redirect
Response (201)
{
  "success": true,
  "payment": {
    "id": "cm4h1abc123def456",
    "status": "PENDING",
    "amount": 99.99,
    "currency": "USD",
    "description": "Order #1234",
    "paymentUrl": "https://fluxpay.online/payment/generic/cm4h1abc123def456",
    "customer": { "email": "customer@example.com", "firstName": "John", "lastName": "Doe" },
    "metadata": { "orderId": "ORD-1234" },
    "createdAt": "2026-10-15T10:30:00.000Z"
  }
}

What happens next

  1. 1The customer opens the paymentUrl and confirms their details.
  2. 2Depending on your account, the customer either enters the card on our page, or is sent on to a partner payment page and enters it there. Either way they enter it once.
  3. 3We send the payment to a route that supports the customer's country, currency and card. If it is declined, we may try another route.
  4. 4You get a webhook with the result, and the customer goes to your success or failure URL.

Skip the Payment Form (autoRedirect)

If you already have the customer's name, email and country, send autoRedirect: true. On accounts that use a hosted payment page, the customer then goes straight to the card page and only types the card.

cURL
curl -X POST https://fluxpay.online/api/v1/payments \
  -H "Authorization: Bearer sk_live_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 25.00,
    "currency": "USD",
    "autoRedirect": true,
    "customer": {
      "email": "customer@example.com",
      "firstName": "Jane",
      "lastName": "Smith",
      "billing": { "country": "DE" }
    },
    "successUrl": "https://yoursite.com/payment/success",
    "failureUrl": "https://yoursite.com/payment/failure"
  }'

With and without autoRedirect

Without (default)With autoRedirect: true
Customer opens paymentUrlSees our page with your data filled in and clicks ProceedSees a short "Redirecting to secure payment..." message
ThenGoes to the card pageGoes straight to the card page
Link reuseReusableSingle-use: one link, one payment
Missing name, email or countryAllowed400 AUTO_REDIRECT_MISSING_FIELDS, nothing is created

When our page is still shown

autoRedirect never leaves the customer stuck. Our page shows instead when:

  • your account takes cards on our page (checkoutMode DIRECT), so the card form is needed
  • you offer more than one payment method, so the customer can choose
  • the amount is outside the card limits (the page explains this)
  • something fails on the way, so the customer sees the error and can retry

Opening the link again while the card page is still open sends the customer back to that same page. In your browser's network tab that call answers resumed: true. This is expected, not an error, and no second payment is started. In test mode the payment completes when you create it, so the link shows it as paid.

Account Configuration

GET /api/v1/config tells you what your account can process and how card checkout works. Pass the customer's country as ?country=XX. Without it we use your business country.

cURL
curl "https://fluxpay.online/api/v1/config?country=DE" \
  -H "Authorization: Bearer sk_live_your_key"
Response
{
  "methods": [
    {
      "method": "CREDIT_CARD",
      "currencies": ["EUR", "USD", "GBP"],
      "countries": ["DE", "FR", "ES"],
      "fxConversion": false
    }
  ],
  "cardBrands": ["VISA", "MASTERCARD", "AMEX", "DISCOVER", "JCB", "DINERS", "UNIONPAY"],
  "checkoutMode": "HOSTED",
  "checkoutModeCountry": "DE",
  "threeDS": {
    "controlAllowed": false,
    "preference": "auto",
    "modes": ["auto", "always", "never"]
  }
}
FieldMeaning
methods[].currenciesCurrencies you can charge. ["*"] means any currency.
methods[].countriesCustomer countries covered. ["*"] means all.
methods[].fxConversiontrue means you can charge in any currency and it is converted for you.
cardBrandsFixed list of card brands the API accepts.
checkoutModeDIRECT: you may collect cards yourself. HOSTED: redirect the customer instead.
threeDS.controlAllowedtrue if you may send the threeDS parameter.

Payment Status

GET /api/v1/payments?id=PAYMENT_ID returns the current state of a payment. Use it to double-check a webhook or a redirect.

StatusMeaning
PENDINGWaiting for the customer or the bank.
COMPLETED or SUCCESSFULPaid. Treat both as success.
FAILEDDeclined or abandoned. See failureReason.
REFUNDED / PARTIALLY_REFUNDEDMoney was returned to the customer.

Direct Card Charges (S2S)

Collect the card on your own form and send it from your server to POST /api/v1/payments/charge.

Before you use this
  • You handle raw card data, so you must be PCI DSS compliant.
  • Card acquiring must be switched on for your account. If not, you get 403 "Card acquiring is not enabled for this merchant".
  • Check that GET /api/v1/config returns checkoutMode: "DIRECT". If it returns HOSTED, the charge sends back a redirect to a payment page where the customer must enter the card again. Use the hosted payment link instead.

Charge a card

SDK
const { payment } = await fluxpay.chargePayment({
  amount: 99.99,
  currency: 'USD',
  card: {
    number: '4242424242424242',
    holderName: 'JOHN DOE',
    expiryMonth: '12',
    expiryYear: '28',
    cvv: '123',
  },
  customer: {
    email: 'john@example.com',
    firstName: 'John',
    lastName: 'Doe',
    ip: '203.0.113.42',
    billing: { country: 'US', city: 'New York', postalCode: '10001' },
  },
  description: 'Order #1234',
  metadata: { orderId: 'ORD-1234' },
  returnUrl: 'https://yoursite.com/payment/return',
});

if (payment.action === 'redirect') {
  // Send the customer to payment.redirectUrl (3DS check or payment page)
} else {
  // Check payment.status: COMPLETED or SUCCESSFUL = paid, FAILED, or PENDING
}
cURL
curl -X POST https://fluxpay.online/api/v1/payments/charge \
  -H "Authorization: Bearer sk_test_your_key" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-1234" \
  -d '{
    "amount": 99.99,
    "currency": "USD",
    "card": {
      "number": "4242424242424242",
      "holderName": "JOHN DOE",
      "expiryMonth": "12",
      "expiryYear": "28",
      "cvv": "123"
    },
    "customer": {
      "email": "john@example.com",
      "firstName": "John",
      "lastName": "Doe",
      "phone": "+12125551234",
      "ip": "203.0.113.42",
      "billing": {
        "address": "123 Main St",
        "city": "New York",
        "state": "NY",
        "postalCode": "10001",
        "country": "US"
      }
    },
    "description": "Order #1234",
    "metadata": { "orderId": "ORD-1234" },
    "returnUrl": "https://yoursite.com/payment/return"
  }'

Parameters

amountnumberrequired
Amount, for example 99.99.
card.numberstringrequired
Card number.
card.holderNamestringrequired
Name on the card.
card.expiryMonthstringrequired
01 to 12.
card.expiryYearstringrequired
2 digits, for example 28. A 4-digit year returns 400.
card.cvvstringrequired
3 or 4 digits.
customer.emailstringrequired
Customer email.
currencystring
3-letter code. Default USD.
customer.firstName / lastName / phonestring
Customer details.
customer.ipstring
Customer IP address, for fraud checks.
customer.billingobject
address, city, state, postalCode, country. If country is missing we use the card's issuing country.
returnUrlstring
Where the customer goes after a redirect.
description / metadata / webhookUrl / affiliateTag / threeDSmixed
Same as on Create a Payment.

Unknown fields are rejected with 400 "Invalid request body".

Responses

Read success and payment in the body. Do not rely on the HTTP status code alone.

Paid
{
  "success": true,
  "payment": {
    "id": "cm4h1abc123",
    "status": "COMPLETED",
    "amount": 99.99,
    "currency": "USD",
    "customer": { "email": "john@example.com" },
    "metadata": { "orderId": "ORD-1234" },
    "createdAt": "2026-10-15T10:00:00.000Z",
    "processedAt": "2026-10-15T10:00:02.000Z"
  }
}
Redirect needed
{
  "success": true,
  "payment": {
    "id": "cm4h1abc123",
    "status": "PENDING",
    "action": "redirect",
    "requires3DS": true,
    "redirectUrl": "https://...",
    "amount": 99.99,
    "currency": "USD"
  }
}
Declined
{
  "success": false,
  "error": "Payment declined",
  "payment": {
    "id": "cm4h1abc123",
    "status": "FAILED",
    "failureReason": "Card declined by issuer."
  }
}
Blocked by a risk rule
{
  "success": false,
  "error": "This transaction was blocked by a risk policy.",
  "code": "POLICY_BLOCKED",
  "action": "BLOCK",
  "supportReference": "POL-9F3K2A1B7C",
  "payment": {
    "id": "cm4h1abc123",
    "status": "FAILED",
    "failureReason": "This transaction was blocked by a risk policy."
  }
}

You can show supportReference to the customer. Our support team uses it to find the case. If no route can take the card at all, the response has code: "NO_ROUTE_AVAILABLE".

Redirects and 3DS

When the response has action: "redirect", send the customer to redirectUrl. This can be a 3DS check from the card's bank, or a payment page where the customer enters the card. requires3DS is also true in both cases, for older integrations.

  1. 1Save payment.id with your order.
  2. 2Redirect the customer to redirectUrl.
  3. 3The customer finishes and comes back to your returnUrl.
  4. 4Get the final status from the webhook, or from GET /api/v1/payments?id=....

Safe retries (Idempotency-Key)

Send an Idempotency-Key header, for example your order ID. If your request times out, you can send it again without charging twice.

First request ended asSame key again
DeclinedAllowed. The charge is tried again.
Paid, pending or redirectRejected with 409 IDEMPOTENCY_REPLAY. No second charge.

Errors

HTTPWhen
400Invalid body, unknown field, 4-digit expiry year, or decimals on a currency without decimals.
401Missing or invalid API key.
403Card acquiring not enabled, threeDS not enabled for your account, or affiliate tracking not enabled.
409Idempotency key already used, or the affiliate tag is inactive.
422No route supports the threeDS mode you asked for (NO_PROVIDER_FOR_3DS_MODE).

Complete example

Node.js
const { FluxPayClient } = require('./fluxpay.cjs.js');
const fluxpay = new FluxPayClient({ apiKey: process.env.FLUXPAY_API_KEY });

async function checkout(order, card) {
  // 1. Only collect cards when the account allows it
  const config = await fetch(
    'https://fluxpay.online/api/v1/config?country=' + order.country,
    { headers: { Authorization: 'Bearer ' + process.env.FLUXPAY_API_KEY } }
  ).then(r => r.json());

  if (config.checkoutMode !== 'DIRECT') {
    const { payment } = await fluxpay.createPayment({
      amount: order.total,
      currency: order.currency,
      customer: { email: order.email, billing: { country: order.country } },
      metadata: { orderId: order.id },
    });
    return { redirect: payment.paymentUrl };
  }

  // 2. Charge the card
  const result = await fluxpay.chargePayment({
    amount: order.total,
    currency: order.currency,
    card,
    customer: { email: order.email, billing: { country: order.country } },
    metadata: { orderId: order.id },
    returnUrl: 'https://yoursite.com/orders/' + order.id,
  });

  if (result.payment.action === 'redirect') {
    return { redirect: result.payment.redirectUrl, paymentId: result.payment.id };
  }
  return { paid: result.success, paymentId: result.payment.id };
}

Refunds

POST /api/v1/refunds refunds all or part of a payment.

cURL
curl -X POST https://fluxpay.online/api/v1/refunds \
  -H "Authorization: Bearer sk_live_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "transactionId": "cm4h1abc123def456",
    "amount": 25.00,
    "reason": "Customer asked for a partial refund"
  }'
Response (201)
{
  "success": true,
  "refund": {
    "id": "cm4h1ref789ghi012",
    "status": "PENDING",
    "amount": 25.00,
    "currency": "USD",
    "type": "PARTIAL",
    "createdAt": "2026-10-15T10:05:00.000Z"
  }
}
transactionIdstringrequired
The payment ID.
amountnumber
Leave it out for a full refund.
reasonstring
Your note.

Live refunds start as PENDING and are reviewed by the FluxPay team before money moves. Test refunds finish right away. GET /api/v1/refunds lists your refunds.

Webhooks

We send a POST request to your URL when a payment changes. Use webhooks, not the redirect, to decide that an order is paid.

Set up

  1. 1In your dashboard, open Webhooks in the sidebar.
  2. 2Enter your URL and pick the events. Test mode and live mode each have their own URL and secret.
  3. 3Copy the webhook secret. You need it to check signatures.

You can also send webhookUrl when you create a payment to use a different URL for that payment.

Events

Event (X-Webhook-Event)Sent when
payment.pendingA payment was created and is waiting.
payment.successThe payment is paid. Fulfil the order.
payment.failedThe payment failed.

Request format

Webhook request
POST https://yoursite.com/webhooks/fluxpay

Headers
  Content-Type: application/json
  X-Webhook-Event: payment.success
  X-Webhook-Signature: <HMAC-SHA256 of the raw body, hex>
  X-Transaction-Id: cm4h1abc123def456
  X-Test-Mode: false          (first attempt only)
  X-Retry-Attempt: 2          (retries only)

Body
{
  "paymentId": "cm4h1abc123def456",
  "transactionId": "cm4h1abc123def456",
  "reference": "cm4h1def456ghi789",
  "amount": 99.99,
  "currency": "USD",
  "status": "COMPLETED",
  "customerEmail": "customer@example.com",
  "testMode": false,
  "metadata": { "orderId": "ORD-1234" },
  "timestamp": "2026-10-15T10:35:00.000Z"
}
  • paymentId is the ID you got when you created the payment.
  • A status of COMPLETED or SUCCESSFUL both mean paid.
  • Failed payments also include failureReason. Payments blocked by a risk rule also include supportReference.

Check the signature

X-Webhook-Signature is an HMAC-SHA256 of the raw request body, using your webhook secret, as hex. Reply with any 2xx status within 10 seconds.

Node.js (Express)
const crypto = require('crypto');

// Use the RAW body. Parsing and re-encoding the JSON breaks the signature.
app.post('/webhooks/fluxpay', express.raw({ type: 'application/json' }), (req, res) => {
  const expected = crypto
    .createHmac('sha256', process.env.FLUXPAY_WEBHOOK_SECRET)
    .update(req.body)
    .digest('hex');
  const received = req.get('X-Webhook-Signature') || '';

  if (received.length !== expected.length ||
      !crypto.timingSafeEqual(Buffer.from(received), Buffer.from(expected))) {
    return res.status(401).end();
  }

  const event = req.get('X-Webhook-Event');
  const payment = JSON.parse(req.body);
  if (event === 'payment.success') {
    // Mark order payment.metadata.orderId as paid
  }
  res.status(200).end();
});
If your endpoint fails
We may retry a failed delivery up to 2 times, after about 1 and 5 minutes. Retries carry an X-Retry-Attempt header. Do not depend on retries: if you miss a webhook, check the payment with GET /api/v1/payments?id=.... Handle the same event twice safely.

Redirect URLs

Send the customer back to your site after they pay.

ParameterUsed when
successUrlThe payment succeeded.
failureUrlThe payment failed.
returnUrlFallback for both, if the one above is not set.

We add these query parameters:

Query parameterValue
statussuccess or failed
transaction_idPayment ID
amountAmount
currencyCurrency code
referenceInternal payment reference
Do not trust the redirect alone
Anyone can open your success URL. Confirm the payment with the webhook or the status API before you ship.

Embed in an iframe

You can show the payment page inside an iframe on your own site. Put the paymentUrl in the iframe. It works with autoRedirect too.

HTML
<iframe
  src="PAYMENT_URL_FROM_THE_API"
  allow="payment"
  style="width: 100%; height: 760px; border: 0;"
></iframe>

<script>
  // Sent once when the payment is finished
  window.addEventListener('message', (event) => {
    if (event.data?.type !== 'fluxpay:payment') return;
    const { status, paymentId } = event.data; // status: "success" or "failed"
    // Close the iframe or move to your own page here
  });
</script>
  • Keep allow="payment". Apple Pay and Google Pay need it. Apple Pay inside an iframe needs Safari 17 or later.
  • When the payment ends, the iframe also opens your successUrl or failureUrl inside the iframe. If you use the message above, you can leave those out.
  • The message is for your page only. Confirm the payment with the webhook or the status API before you ship.
  • A few banks do not allow their 3DS check inside an iframe. If a customer gets stuck there, open the same paymentUrl as a full page.

Payment Methods

What your customers see depends on what is switched on for your account. Use GET /api/v1/config to see the methods, currencies and countries you can take.

MethodHow the customer pays
Cards (Visa, Mastercard and others)On the payment page, or on your own form with direct charges.
Apple Pay and Google PayOn the payment page, where available.
Bank payments (open banking)The customer picks their bank and approves in their banking app.
Local payment methodsSome countries have local methods. The customer finishes on a partner payment page.

Affiliate Tracking

Tag payments with your own affiliates or partners, then see totals per affiliate. New tags are created automatically the first time you use them.

Ask us to switch it on
Affiliate tracking is enabled per account. Contact your account manager or support@fluxpay.online. Until then, sending an affiliate field returns 403 "Affiliate tracking is not enabled for this merchant".
Affiliates page in the FluxPay dashboard, with totals per affiliate and an Untagged row.
Dashboard > Affiliates

1. Tag a payment

Add affiliateTag to POST /api/v1/payments, POST /api/v1/payments/charge or a payment link. Tags are made lowercase, spaces become hyphens, and they are cut to 64 characters. So "Partner A" and "partner-a" are the same affiliate.

SDK
const { payment } = await fluxpay.createPayment({
  amount: 149.99,
  currency: 'USD',
  customer: { email: 'customer@example.com' },
  affiliateTag: 'partner-a',
});
cURL
curl -X POST https://fluxpay.online/api/v1/payments \
  -H "Authorization: Bearer sk_live_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 149.99,
    "currency": "USD",
    "customer": { "email": "customer@example.com" },
    "affiliateTag": "partner-a"
  }'

2. Get totals

GET /api/v1/affiliates/breakdown returns the same numbers as the dashboard. from and to default to the last 30 days (max 366 days). groupBy is day, week or month. Add affiliateId to get one affiliate.

cURL
curl "https://fluxpay.online/api/v1/affiliates/breakdown?from=2026-09-01&to=2026-09-30&groupBy=week" \
  -H "Authorization: Bearer sk_live_your_key"
Response
{
  "rows": [
    {
      "affiliateId": "aff_123",
      "tag": "partner-a",
      "displayName": "Partner A",
      "summary": {
        "grossVolume": 150.00,
        "transactionCount": 3,
        "successfulCount": 1,
        "failedCount": 1,
        "pendingCount": 1,
        "successRate": 0.5,
        "averageTicket": 150.00,
        "totalFees": 7.50,
        "netAmount": 117.50,
        "refundCount": 1, "refundVolume": 25.00,
        "chargebackCount": 0, "chargebackVolume": 0.00
      },
      "buckets": [ { "period": "2026-09-01", "grossVolume": 150.00 } ]
    }
  ],
  "totals": { "grossVolume": 350.00 }
}

netAmount is gross minus fees, refunds and chargebacks. Payments without a tag are grouped in a row with tag: "untagged", when there are any.

3. Manage affiliates

RequestDoes
GET /api/v1/affiliatesList affiliates
POST /api/v1/affiliatesCreate one: { tag, displayName? }
PATCH /api/v1/affiliates/{id}Rename, or set isActive
DELETE /api/v1/affiliates/{id}Deactivate. History stays in reports.

A deactivated tag cannot be used on new payments (409).

WooCommerce Plugin

Install the plugin to take payments in a WooCommerce store. No coding needed. Customers are sent to the hosted payment page.

RequirementVersion
WordPress5.8 or later
WooCommerce7.0 or later
PHP7.4 or later
HTTPSRequired

Install

  1. 1In WordPress go to Plugins > Add New > Upload Plugin, upload the zip, install and activate.
  2. 2Go to WooCommerce > Settings > Payments > FluxPay.
  3. 3Paste your Test API Key and Live API Key, and your webhook secret from the FluxPay dashboard.
  4. 4Tick Enable. Turn off Test Mode when you are ready for real payments.

Settings

SettingWhat it doesDefault
Enable/DisableShow FluxPay at checkoutOff
TitleName shown at checkoutCredit Card (FluxPay)
DescriptionText shown at checkoutPay securely with your credit card.
Test ModeUse the test key and test URLOn
Test API KeyYour sk_test_ keyEmpty
Test Base URLAPI address used in test modeChange it to https://fluxpay.online
Live API KeyYour sk_live_ keyEmpty
Live Base URLAPI address used in live modehttps://fluxpay.online
Webhook SecretUsed to check webhook signaturesEmpty
LoggingWrite logs to WooCommerce > Status > LogsOff

The plugin sends its own webhook URL (yoursite.com/?wc-api=fluxpay) with every payment, so you only need to paste the webhook secret. Unsigned webhooks are rejected.

How a payment works

  1. 1The customer clicks Place order.
  2. 2The plugin creates a FluxPay payment and sends the customer to the payment page.
  3. 3The customer pays and comes back to your order confirmation page.
  4. 4Our webhook updates the order to Processing, Failed or Refunded.

Troubleshooting

Customer sees an error instead of the payment page

Check the API key and base URL for the current mode, and that your account is active. Turn on Logging and check WooCommerce > Status > Logs.

Order stays on Pending payment

Check the webhook secret, and that your site is reachable over HTTPS and not blocked by a firewall.

Own integration: check a webhook in PHP
<?php
// Only needed if you build your own integration.
// The FluxPay WooCommerce plugin already does this for you.

function handle_fluxpay_webhook(WP_REST_Request $request) {
    // Use the raw body. Re-encoding parsed JSON will not match the signature.
    $body      = file_get_contents('php://input');
    $signature = $request->get_header('X-Webhook-Signature');
    $event     = $request->get_header('X-Webhook-Event');
    $secret    = get_option('fluxpay_webhook_secret');

    if (!hash_equals(hash_hmac('sha256', $body, $secret), (string) $signature)) {
        return new WP_REST_Response(['error' => 'Invalid signature'], 401);
    }

    $payload = json_decode($body, true);
    $order   = wc_get_order($payload['metadata']['orderId'] ?? 0);

    if ($order && $event === 'payment.success') {
        $order->payment_complete($payload['paymentId']);
    }

    return new WP_REST_Response(['success' => true], 200);
}

Shopify

Coming soon.

Tell me when it is ready
    FluxPay — Documentation