What is FluxPay?
FluxPay is a modern payment platform built to facilitate simple, fast, and secure payments. Our platform provides a single integration point for merchants to accept payments with intelligent routing, automatic failover, and multi-provider support.
Intelligent Routing
Automatic provider selection and failover for maximum approval rates.
Secure Payments
Encrypted transport and hosted checkout that keeps raw card data off merchant servers.
Simple Integration
Clean REST API with comprehensive documentation.
Multiple Payment Methods
Accept cards, Open Banking, and Asia local payments (HKD, JPY, KRW) through a single API.
SDK Installation
Load the FluxPay SDK directly from our CDN. No npm install needed — just add a script tag.
Browser (Script Tag)
Add this to your HTML — works on any website. The integrity attribute ensures the file hasn't been tampered with.
<script src="https://fluxpay.online/sdk/v1/fluxpay.min.js"
integrity="sha384-..."
crossorigin="anonymous"></script>
<!-- FluxPay is now available as window.FluxPay -->
<!-- Create payments server-side, then redirect: -->
<script>
// Call YOUR backend to create a payment (never expose API keys here)
fetch('/api/create-payment', { method: 'POST', body: JSON.stringify({ amount: 99.99 }) })
.then(r => r.json())
.then(({ paymentUrl }) => window.location.href = paymentUrl);
</script>Node.js (CommonJS)
// Download the SDK bundle
// 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_live_* or sk_test_*
});Node.js (ESM)
// Download: curl -o fluxpay.esm.mjs https://fluxpay.online/sdk/v1/fluxpay.esm.mjs
import { FluxPayClient } from './fluxpay.esm.mjs';
const fluxpay = new FluxPayClient({
apiKey: process.env.FLUXPAY_API_KEY, // sk_live_* or sk_test_*
});SRI Integrity Verification
Get the latest integrity hashes for all SDK files:
GET https://fluxpay.online/api/v1/sdk/integrityBrowser Usage
paymentUrl to the browser.Recommended pattern: your frontend calls your backend, which creates the payment server-side and returns the checkout URL.
Your Backend (Node.js)
// POST /api/create-payment
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 Frontend (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',
firstName: 'John',
lastName: 'Doe',
},
description: 'Order #1234',
}),
});
const { paymentUrl } = await res.json();
window.location.href = paymentUrl;
});
</script>Node.js Usage
Use the SDK in your Express.js or Node.js backend to create payments server-side.
const express = require('express');
const { FluxPayClient } = require('./fluxpay.cjs.js');
const app = express();
app.use(express.json());
const fluxpay = new FluxPayClient({
apiKey: process.env.FLUXPAY_API_KEY,
});
// Create a payment
app.post('/checkout', async (req, res) => {
const { amount, email, firstName, lastName, description } = req.body;
// Basic validation
if (!amount || amount <= 0 || amount > 50000) {
return res.status(400).json({ error: 'Invalid amount' });
}
if (!email || !firstName || !lastName) {
return res.status(400).json({ error: 'Missing customer details' });
}
try {
const { payment } = await fluxpay.createPayment({
amount,
currency: 'USD',
customer: { email, firstName, lastName },
description: description || 'Payment',
returnUrl: 'https://yoursite.com/payment/callback',
successUrl: 'https://yoursite.com/payment/success',
failureUrl: 'https://yoursite.com/payment/failure',
});
res.json({ paymentUrl: payment.paymentUrl });
} catch (err) {
res.status(500).json({ error: err.message });
}
});
// Check payment status
app.get('/payment-status/:id', async (req, res) => {
try {
const { payment } = await fluxpay.getPayment(req.params.id);
res.json({ status: payment.status });
} catch (err) {
res.status(500).json({ error: err.message });
}
});
app.listen(3000);TypeScript Support
The SDK supports TypeScript with type definitions. Import types directly:
import { FluxPayClient, FluxPayError } from './fluxpay.esm.mjs';
import type { CreatePaymentRequest, Payment, ChargePaymentRequest } from './fluxpay.esm.mjs';
const fluxpay = new FluxPayClient({ apiKey: process.env.FLUXPAY_API_KEY! });
// Hosted checkout flow
async function createOrder(req: CreatePaymentRequest): Promise<Payment> {
const { payment } = await fluxpay.createPayment(req);
return payment;
}
// S2S charge with error handling
async function chargeCard(charge: ChargePaymentRequest): Promise<Payment> {
try {
const { payment } = await fluxpay.chargePayment(charge);
if (payment.requires3DS && payment.redirectUrl) {
// Redirect customer for 3DS verification
return payment;
}
return payment;
} catch (err) {
if (err instanceof FluxPayError) {
console.error(`FluxPay error [${err.statusCode ?? 'network'}]: ${err.message}`);
}
throw err;
}
}Quick Start (cURL)
No SDK? Use any HTTP client to integrate with FluxPay via the REST API directly.
# No installation needed - cURL is pre-installed on most systems
# Your API key from Dashboard → Settings → API Keys
# Test mode: sk_test_* | Live mode: sk_live_*API Keys
Generate your authentication credentials to start making API calls.
Test vs Live Keys
sk_test_*TEST MODEUse for development and testing. Routes to DEMO provider (no real money).
sk_live_*LIVE MODEUse for production payments. Routes to real payment providers.
How to Generate
- 1Login to your FluxPay Dashboard
- 2Navigate to Settings → API Keys
- 3Toggle Test Mode or Live Mode
- 4Click "Rotate API Key" to generate a new key
- !Copy immediately — you can't view it again!
Security Best Practices
FLUXPAY_API_KEY=sk_live_your_key_hereNote: No rate limiting is currently enforced on the FluxPay API. Please still apply sensible client-side throttling and retry/back-off logic on your side.
Test Cards
Use these test card numbers with your test API key (sk_test_*) to simulate different payment outcomes. Any future expiry date and any 3-digit CVV will work.
| Card Number | Brand | Outcome | Description |
|---|---|---|---|
| 4111 1111 1111 0000 | Visa | Success | Payment completes instantly |
| 4111 1111 1111 1111 | Visa | Decline | Card declined by provider |
| 4111 1111 1111 2222 | Visa | 3DS Redirect | Triggers 3D Secure authentication flow |
| 4111 1111 1111 3333 | Visa | Pending | Stays pending, webhook arrives later |
| 5555 5555 5555 0000 | Mastercard | Success | Mastercard success |
| 5555 5555 5555 1111 | Mastercard | Decline | Mastercard decline |
Pattern Rule
The last 4 digits of the card number determine the outcome. Any card number ending in 0000 succeeds, 1111 declines, 2222 triggers 3DS, and 3333 stays pending. All other endings return success.
The table above is representative, not exhaustive — the same outcomes are also triggered by 1234/9999 (decline), 2233/2244 (3DS), and 3344/3355 (pending) endings.
Testing 3DS
- 1Create a payment or S2S charge using a card ending in
2222(e.g.4111111111112222) - 2The API response returns
requires3DS: truewith aredirectUrl - 3Redirect the customer (or navigate in your test) to the
redirectUrl— this opens the 3DS simulator page - 4On the simulator, click "Approve" to complete the payment or "Reject" to fail it
- 5The customer is redirected back to your
returnUrland a webhook is sent with the final status
Test Mode Only
The 3DS simulator and test card outcomes only work with test API keys (sk_test_*). Live API keys route to real payment providers and real 3D Secure authentication.
Create a Payment
Send a POST request to create a payment link. The response includes a payment URL to redirect your customer.
sk_live_*), merchants whose onboarding status is SUSPENDED, PENDING, KYB_IN_PROGRESS, or KYB_SUBMITTED get 403 { "error": "merchant_not_active" } instead of a payment. Test mode (sk_test_*) bypasses this gate.Payment Parameters
Required Parameters
amountnumberPayment amount in decimal format (e.g., 99.99). Must be greater than 0.
customer.emailstringCustomer email address. Used for payment receipts and customer identification.
Optional Parameters
currencystringCurrency code (USD, EUR, GBP, CAD, AUD, HKD, JPY, KRW, etc.). Defaults to USD. Must be supported by at least one of your assigned payment providers.
descriptionstringPayment description. Helps you and your customer identify the payment purpose.
customer.firstNamestringCustomer first name. Recommended — required by most payment providers for compliance.
customer.lastNamestringCustomer last name. Recommended — required by most payment providers for compliance.
customer.phonestringCustomer phone number. Pre-fills the phone field on the payment form.
customer.ipstringCustomer IP address. Used by payment providers for fraud detection and geo-validation. If omitted, extracted from request headers or defaults to a safe European IP.
customer.billingobjectBilling address object. Pre-fills billing address fields on the payment form.
Fields: address, city, state, country, postalCode
billing.country (ISO 2, e.g. BG) drives provider routing — send it whenever you can. If omitted, we derive the country from the card's issuing BIN; if that is unknown too, routing falls back to your account's aggregators. Call GET /api/v1/config to see which countries your providers cover.
languagestringPayment form language. Defaults to en. Supported: en (English), fr (French)
Webhook & Tracking
webhookUrlstringCustom webhook URL for this specific payment. Overrides your default webhook URL configured in the dashboard.
metadataobjectCustom data object attached to the payment. Store any custom information (order IDs, user IDs, etc.) — returned in webhooks and API responses.
returnUrlstringGeneral redirect URL for both success and failure outcomes.
successUrlstringSuccess-specific redirect. Overrides returnUrl for successful payments.
failureUrlstringFailure-specific redirect. Overrides returnUrl for failed payments.
SDK (JavaScript)
const fluxpay = new FluxPayClient({
apiKey: process.env.FLUXPAY_API_KEY, // sk_live_* or sk_test_*
});
const { payment } = await fluxpay.createPayment({
amount: 99.99,
currency: 'USD',
customer: {
email: 'customer@example.com',
firstName: 'John',
lastName: 'Doe',
},
description: 'Order #1234',
metadata: {
orderId: 'ORD-2024-001',
},
});
// Redirect customer to payment page
window.location.href = payment.paymentUrl;cURL (HTTP API)
curl -X POST https://fluxpay.online/api/v1/payments \
-H "Authorization: Bearer sk_live_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"amount": 99.99,
"currency": "USD",
"customer": {
"email": "customer@example.com",
"firstName": "John",
"lastName": "Doe",
"phone": "+1-212-555-1234",
"ip": "203.0.113.42",
"billing": {
"address": "123 Main Street",
"city": "New York",
"state": "NY",
"country": "US",
"postalCode": "10001"
}
},
"description": "Order #1234",
"language": "fr",
"webhookUrl": "https://yoursite.com/webhooks/fluxpay",
"metadata": {
"orderId": "ORD-2024-001",
"userId": "user_12345"
}
}'
# Response:
# {
# "success": true,
# "payment": {
# "id": "cm4h1abc123def456",
# "status": "PENDING",
# "amount": 99.99,
# "currency": "USD",
# "paymentUrl": "https://fluxpay.online/payment/generic/cm4h1abc123def456",
# "customer": { ... },
# "createdAt": "2025-01-23T10:30:00Z"
# }
# }What Happens After Creation?
How cascade/routing works: a single charge can try many providers before it resolves — up to 20 attempts total, 2 attempts per provider, card cascade first (2D), then 3D fallback if needed. This is why one charge can show up in the logs of several providers, and why processing time varies per attempt rather than being a fixed, single round-trip.
Supported Currencies & Countries
Before you charge, call this endpoint to see what your account can process — the currencies and customer countries covered by your active providers, plus the card brands accepted. Use it to validate a currency/country up front instead of hitting NO_ROUTE_AVAILABLE.
curl https://fluxpay.online/api/v1/config \
-H "Authorization: Bearer sk_live_xxx"Response
{
"methods": [
{
"method": "CREDIT_CARD",
"currencies": ["*"], // "*" = any currency (converted to the provider's currency)
"countries": ["AT", "BE", "BG", "DE", "FR", "..."], // card coverage — NOT necessarily every country
"fxConversion": true // this method auto-converts currencies
},
{
"method": "APM",
"currencies": ["USD", "EUR", "BRL", "..."],
"countries": ["*"], // this method may cover all countries
"fxConversion": false
}
],
"cardBrands": ["VISA", "MASTERCARD", "AMEX", "DISCOVER", "JCB", "DINERS", "UNIONPAY"]
}Per payment method. Coverage is broken out by method (e.g. CREDIT_CARD, APM) because they differ — cards may be limited to specific countries while an alternative method covers more. Check the entry for the method you intend to use.
Currency conversion. When a method's fxConversion is true (or its currencies is ["*"]), you may charge in any currency — we convert it to a currency the selected provider accepts and settle the difference. Routing is still gated by that method's countries: send customer.billing.country, or we infer it from the card BIN. ["*"] means all countries.
Payment Methods
Multiple ways for your customers to pay.
Credit & Debit Cards
Default MethodAccept all major credit and debit cards with 3D Secure authentication support.
New acquirer: PacePay card acquiring (hosted 3DS) has joined the cascade. Routing is automatic — no integration change is required on your side.
Open Banking
Bank TransfersInstant bank-to-bank payments via Open Banking. Lower fees than cards with direct settlement.
Asia Payments
Regional MethodsAccept local payment methods across Asia — bank transfers, virtual accounts, and credit cards in local currencies.
How It Works
Open Banking Availability
Open Banking enables bank-to-bank transfers — customers pay directly from their bank account without entering card details. When available, the payment page automatically shows a Bank Transfer tab alongside the card form.
- • EU (27 countries) — EUR payments
- • United Kingdom (GB) — GBP payments
- • Australia (AU) — AUD payments
- • Canada (CA) — CAD payments
- 1. Customer selects Bank Transfer tab
- 2. Redirected to bank's secure login page
- 3. Authorises payment in their banking app
- 4. Redirected back — payment confirmed instantly
Note: Open Banking is enabled per-account based on your assigned providers and the customer's country. The Bank Transfer tab only appears when the customer's country is supported by your active providers. Contact support to enable Open Banking for your account.
Asia Payments — Supported Currencies & Methods
Accept payments in local Asian currencies via bank transfers, virtual accounts, and credit cards. Customers are redirected to a secure hosted payment page to complete the transaction.
| Currency | Country | Payment Method | Limits |
|---|---|---|---|
| HKD | Hong Kong (HK) | Bank Transfer | 100 – 100,000 HKD |
| JPY | Japan (JP) | Credit Card (JCB) | 100 – 1,000,000 JPY |
| KRW | South Korea (KR) | Virtual Account | 1,000 – 500,000 KRW |
- 1. Create a payment link with
HKD,JPY, orKRWcurrency - 2. Customer opens the payment page and fills their details
- 3. Customer is redirected to a secure hosted payment page
- 4. Payment is confirmed via webhook — no card data touches your server
curl -X POST https://fluxpay.online/api/v1/payments \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{
"amount": 500,
"currency": "HKD",
"customer": {
"email": "customer@example.com",
"firstName": "Wing",
"lastName": "Chan",
"phone": "+852-9123-4567",
"billing": {
"address": "88 Queensway",
"city": "Hong Kong",
"state": "HK",
"country": "HK",
"postalCode": "000000"
}
}
}'curl -X POST https://fluxpay.online/api/v1/payments \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{
"amount": 5000,
"currency": "JPY",
"customer": {
"email": "tanaka@example.jp",
"firstName": "Yuki",
"lastName": "Tanaka",
"phone": "+81-90-1234-5678",
"billing": {
"address": "1-1 Marunouchi",
"city": "Tokyo",
"state": "TK",
"country": "JP",
"postalCode": "1000005"
}
}
}'curl -X POST https://fluxpay.online/api/v1/payments \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{
"amount": 50000,
"currency": "KRW",
"customer": {
"email": "kim@example.kr",
"firstName": "Minjun",
"lastName": "Kim",
"phone": "+82-10-1234-5678",
"billing": {
"address": "123 Gangnam-daero",
"city": "Seoul",
"state": "SE",
"country": "KR",
"postalCode": "06164"
}
}
}'Note: JPY and KRW do not support decimal amounts — use whole numbers only (e.g., 5000 not 50.00). The response includes a paymentUrl — redirect the customer there to complete payment.
Configure Webhooks
Receive real-time payment notifications.
Two Ways to Configure
Set a default webhook URL in your dashboard:
- 1. Go to Settings → Webhooks
- 2. Enter your webhook URL
- 3. Select events to receive
- 4. Copy webhook secret for signature verification
Include webhookUrl in your API request:
webhookUrl: "https://your-app.com/webhooks"Webhook Events
payment.successPayment completed successfully — fulfill order and send confirmation
payment.failedPayment failed after all retry attempts — notify customer
payment.pendingPayment processing — update order status to "processing"
payment.refundedFull refund processed — update order to refunded
payment.partially_refundedPartial refund processed — amount field reflects the refunded amount
payment.disputedChargeback or dispute opened on this payment
dispute.wonDispute resolved in your favour
dispute.lostDispute resolved against you — funds returned to customer
dispute.evidence_requiredYou must submit evidence to defend this dispute
Failed webhooks are automatically retried: after 1 min, 5 min, 15 min. Maximum 3 retry attempts. Retried requests include an X-Retry-Attempt header with the attempt number. The X-Test-Mode header is sent on the initial delivery attempt only — it is not resent on retries.
# WEBHOOK PAYLOAD EXAMPLE (payment.failed)
# POST https://yoursite.com/webhooks/fluxpay
# Headers:
# Content-Type: application/json
# X-Webhook-Signature: <64-char lowercase hex, HMAC-SHA256 of raw body>
# X-Webhook-Event: payment.failed ← event type is here
# X-Transaction-Id: cm4h1abc123def456
# X-Test-Mode: false ← only sent on the initial delivery, NOT on retries
# X-Retry-Attempt: 2 ← only present on retries (attempt 2, 3, 4)
# The request body IS the payload — no wrapper object:
{
"transactionId": "cm4h1abc123def456",
"reference": "cm4h1def456ghi789",
"amount": 99.99,
"currency": "USD",
"status": "FAILED",
"failureReason": "Card declined by issuer.",
"supportReference": "POL-9F3K2A1B7C",
"customerEmail": "customer@example.com",
"testMode": false,
"metadata": {
"orderId": "ORD-2024-001",
"userId": "user_12345"
},
"timestamp": "2025-01-23T10:35:00Z"
}
# YOUR WEBHOOK ENDPOINT MUST:
# 1. Read the raw request body (before JSON parsing) for signature verification
# 2. Verify the X-Webhook-Signature header (HMAC-SHA256 of raw body bytes, hex digest)
# 3. Read the event type from X-Webhook-Event header (NOT from the body)
# 4. Process the event (fulfill order, send email, etc.)
# 5. Return 200 OK within 10 secondsfailureReason is included whenever the payment failed. supportReference is included only when a policy/fraud rule (not a plain provider decline) blocked the payment — quote it when contacting support. Both fields are omitted entirely on payment.success payloads.
Affiliate Tracking
NewAttribute payment links and transactions to your own affiliates or sub-partners, then see a full-financials breakdown per affiliate over any time period — in your dashboard and over the API. Tags are free-form and auto-register on first use, so there is no setup step.
Enabling affiliate tracking
Affiliate tracking is enabled per account by the FluxPay team. Contact your account manager or support@fluxpay.online to switch it on. Once enabled, the Affiliates page appears in your dashboard and the affiliate fields below become active on the API.
While disabled, any affiliate parameter returns 403 — Affiliate tracking is not enabled for this merchant rather than failing silently.

1. Tag a payment or link via the API
Add an optional affiliateTag when you create a payment, a payment link, or an S2S charge. Any string works — an unknown tag auto-creates the affiliate. Tags are normalized (lowercased, trimmed), so "Partner A" and "partner-a" are the same affiliate.
SDK (JavaScript)
const { payment } = await fluxpay.createPayment({
amount: 149.99,
currency: 'USD',
customer: {
email: 'customer@example.com',
firstName: 'Jane',
lastName: 'Smith',
},
affiliateTag: 'partner-a', // attribute to this affiliate
});cURL (HTTP API)
curl -X POST https://fluxpay.online/api/v1/payments \
-H "Authorization: Bearer sk_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"amount": 149.99,
"currency": "USD",
"customer": { "email": "customer@example.com" },
"affiliateTag": "partner-a"
}'The same affiliateTag field is accepted on POST /api/v1/payments, POST /api/v1/payments/charge (S2S), and POST /api/merchant/payment-links.
2. Read the breakdown via the API
Pull the same numbers the dashboard shows. Group by day, week (Monday-start), or month, over any range up to 366 days.
curl "https://fluxpay.online/api/v1/affiliates/breakdown?from=2026-07-01&to=2026-07-31&groupBy=day" \
-H "Authorization: Bearer sk_live_xxx"Response
{
"rows": [
{
"affiliateId": "aff_...",
"tag": "partner-a",
"displayName": "Partner A",
"summary": {
"grossVolume": 150.00,
"transactionCount": 2,
"successfulCount": 1,
"failedCount": 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-07-28", "grossVolume": 150.00, "...": "..." } ]
}
],
"totals": { "grossVolume": 350.00, "...": "..." }
// an "Untagged" row (affiliateId: null) is always included so totals reconcile
}3. Manage affiliates via the API
Full CRUD is available. Deactivating is soft — it blocks new tagging with that tag but keeps all history in reports.
/api/v1/affiliates— list affiliates/api/v1/affiliates— create { tag, displayName? }/api/v1/affiliates/{id}— rename / toggle isActive/api/v1/affiliates/{id}— soft-deactivatePrefer the dashboard? Everything above is also on the Affiliates page — pick a period, sort the breakdown table, expand a row for its time-series, and create/rename/deactivate affiliates inline. The affiliate field also appears on the payment-link form as a combobox.
Redirect URLs
Return customers to your site after payment completion.
Redirect URL Options
returnUrlstring (optional)General redirect URL for both success and failure.
successUrlstring (optional)Success-specific redirect. Overrides returnUrl for successful payments.
failureUrlstring (optional)Failure-specific redirect. Overrides returnUrl for failed payments.
Query Parameters Received
These query parameters are appended to your redirect URL:
status— "success" or "failed"transaction_id— Unique transaction identifieramount— Payment amountcurrency— Currency code (USD, EUR, etc.)reference— External reference IDTip: Always verify payment status via webhook or API — do not rely solely on redirect URL parameters.
curl -X POST https://fluxpay.online/api/v1/payments \
-H "Authorization: Bearer sk_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"amount": 99.99,
"currency": "USD",
"customer": {
"email": "customer@example.com",
"firstName": "John",
"lastName": "Doe"
},
"description": "Order #1234",
"metadata": {
"orderId": "ORD-2024-001"
},
"returnUrl": "https://yoursite.com/payment/callback",
"successUrl": "https://yoursite.com/payment/success",
"failureUrl": "https://yoursite.com/payment/failure"
}'
# After payment, customer is redirected to:
# SUCCESS: https://yoursite.com/payment/success?status=success&transaction_id=...
# FAILURE: https://yoursite.com/payment/failure?status=failed&transaction_id=...Embed Payment Page
Embed FluxPay checkout directly in your website.
Redirect (Recommended)
Redirect customer to FluxPay hosted page. Best for security and compliance.
Best for: Most use cases
Modal iFrame
Embed payment page in a popup modal. Keeps customer on your site during checkout.
Best for: SaaS, subscriptions
Inline iFrame
Embed payment form directly in your page layout. Seamless checkout experience.
Best for: Single-page apps
<!-- OPTION 1: Inline iFrame -->
<iframe
src="https://fluxpay.online/payment/generic/cm4h1abc123def456"
width="100%"
height="600"
frameborder="0"
allow="payment"
style="border: none; border-radius: 12px;"
></iframe>
<!-- OPTION 2: Modal/Popup iFrame -->
<div id="payment-modal" style="
display: none;
position: fixed;
top: 0; left: 0;
width: 100vw; height: 100vh;
background: rgba(0,0,0,0.5);
z-index: 9999;
">
<div style="
position: absolute;
top: 50%; left: 50%;
transform: translate(-50%, -50%);
width: 90%; max-width: 500px;
background: white;
border-radius: 16px;
overflow: hidden;
">
<iframe id="payment-iframe" width="100%" height="600" frameborder="0"></iframe>
</div>
</div>
<script>
function openPayment(paymentUrl) {
document.getElementById('payment-iframe').src = paymentUrl;
document.getElementById('payment-modal').style.display = 'block';
}
</script>// React Component for Embedded Payment
import { useState } from 'react';
function PaymentModal({ paymentUrl, onClose }) {
return (
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50">
<div className="bg-white rounded-2xl w-full max-w-lg overflow-hidden">
<div className="flex justify-between items-center p-4 border-b">
<h3 className="font-semibold">Complete Payment</h3>
<button onClick={onClose} className="text-muted-foreground hover:text-gray-700">✕</button>
</div>
<iframe
src={paymentUrl}
className="w-full h-[600px] border-0"
allow="payment"
/>
</div>
</div>
);
}
// Usage in your checkout page
function CheckoutPage() {
const [paymentUrl, setPaymentUrl] = useState(null);
const handleCheckout = async () => {
const res = await fetch('/api/create-payment', { method: 'POST' });
const { paymentUrl } = await res.json();
setPaymentUrl(paymentUrl);
};
return (
<div>
<button onClick={handleCheckout}>Pay Now</button>
{paymentUrl && (
<PaymentModal
paymentUrl={paymentUrl}
onClose={() => setPaymentUrl(null)}
/>
)}
</div>
);
}Important Notes for Embedding
S2S (Server-to-Server) Payments
For PCI-compliant merchants who collect card details on their own checkout form. Send card data directly via API — no hosted page redirect needed.
PCI Compliance Required
S2S integration puts raw card data in your environment, so you must meet the PCI DSS obligations applicable to that environment. If you are not approved for direct card handling, use the Hosted Checkout integration instead.
Hosted Checkout
- Customer redirected to FluxPay form
- FluxPay handles card data
- Lower PCI scope; complete the PCI validation applicable to your integration
- Best for most merchants
S2S (Direct Charge)
- Merchant collects card on own form
- Card data sent via API
- PCI DSS compliance required
- Best for high-volume merchants
Charge Payment
POST /api/v1/payments/charge
cardAcquiringEnabled on your merchant account. If it is not enabled you get 403 "Card acquiring is not enabled for this merchant" instead of a charge attempt. Contact support to enable it.SDK (JavaScript)
const { payment } = await fluxpay.chargePayment({
amount: 99.99,
currency: 'USD',
card: {
number: '4111111111111111',
holderName: 'JOHN DOE',
expiryMonth: '12',
expiryYear: '25',
cvv: '123',
},
customer: {
email: 'john@example.com',
firstName: 'John',
lastName: 'Doe',
phone: '+1234567890',
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-001' },
});
if (payment.requires3DS) {
// Redirect for 3DS authentication
window.location.href = payment.redirectUrl;
} else {
console.log('Payment:', payment.status);
}cURL (HTTP API)
curl -X POST https://fluxpay.online/api/v1/payments/charge \
-H "Authorization: Bearer sk_live_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"amount": 99.99,
"currency": "USD",
"card": {
"number": "4111111111111111",
"holderName": "JOHN DOE",
"expiryMonth": "12",
"expiryYear": "25",
"cvv": "123"
},
"customer": {
"email": "john@example.com",
"firstName": "John",
"lastName": "Doe",
"phone": "+1234567890",
"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-001" }
}'Request Parameters
amountnumberTransaction amount (e.g., 99.99)
currencystringISO 4217 currency code (default: USD)
card.numberstringCard number (13-19 digits)
card.holderNamestringCardholder name as on card
card.expiryMonthstringExpiry month (01-12)
card.expiryYearstringExpiry year — must be 2-digit (e.g., 25, not 2025). A 4-digit year returns 400 "Invalid expiry year. Must be 2-digit year (e.g., 25)".
card.cvvstringCVV/CVC (3-4 digits)
customer.emailstringCustomer email address
customer.firstNamestringCustomer first name
customer.lastNamestringCustomer last name
customer.phonestringPhone with country code
customer.ipstringCustomer IP for fraud/geo checks
customer.billingobjectBilling address (address, city, state, postalCode, country)
descriptionstringPayment description
metadataobjectCustom key-value metadata
webhookUrlstringOverride webhook URL for this payment
returnUrlstringReturn URL after 3DS redirect
affiliateTagstringAttribute this charge to an affiliate/sub-partner tag (see Affiliate Tracking)
Response — Success (2D)
{
"success": true,
"payment": {
"id": "cm4h1abc123",
"status": "COMPLETED",
"amount": 99.99,
"currency": "USD",
"provider": "XSPEND",
"customer": {
"email": "john@example.com",
"firstName": "John",
"lastName": "Doe"
},
"metadata": { "orderId": "ORD-001" },
"createdAt": "2026-03-24T10:00:00.000Z",
"processedAt": "2026-03-24T10:00:02.000Z"
}
}Response — 3DS Required
{
"success": true,
"payment": {
"id": "cm4h1abc123",
"status": "PENDING",
"requires3DS": true,
"redirectUrl": "https://provider.com/3ds?session=xxx",
"amount": 99.99,
"currency": "USD"
}
}Response — Declined
{
"success": false,
"error": "Payment declined",
"payment": {
"id": "cm4h1abc123",
"status": "FAILED",
"failureReason": "Card declined by issuer."
}
}Response — Declined (policy/fraud block)
When a business-rule or fraud/risk policy — not the card issuer — blocks the charge, the response carries extra fields so you can show the customer a clear reason and a support reference:
{
"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."
}
}code and action come from the policy that fired; supportReference is safe to show to the customer and quote when contacting support. Ordinary provider declines (above) do not include these fields.
Idempotency
Send an Idempotency-Key header on POST /api/v1/payments/charge to safely retry a request (e.g. after a network timeout) without double-charging the customer.
curl -X POST https://fluxpay.online/api/v1/payments/charge \
-H "Authorization: Bearer sk_live_your_api_key_here" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-1234-attempt-1" \
-d '{
"amount": 99.99,
"currency": "USD",
"card": { "number": "4111111111111111", "holderName": "JOHN DOE", "expiryMonth": "12", "expiryYear": "25", "cvv": "123" },
"customer": { "email": "john@example.com" }
}'
# Replaying the same key while the original request is still resolving
# (or after it already resolved) returns, without charging again:
# HTTP 409
# {
# "error": "Duplicate request for this Idempotency-Key",
# "code": "IDEMPOTENCY_REPLAY"
# }Key lifecycle: a decline releases the key — the same key can be retried with a corrected request. A success, a pending status, or a 3DS redirect in flight all keep the key claimed, so retrying with that key while the original charge is still resolving (or already succeeded) is rejected with 409 rather than firing a second charge.
3DS Handling
Some transactions require 3D Secure authentication. When this happens, the response will includerequires3DS: true and aredirectUrl.
3DS Flow
- 1Call
POST /api/v1/payments/chargewith card details - 2Check response — if
requires3DSis true, redirect the customer toredirectUrl - 3Customer completes 3DS on the provider's page
- 4Customer returns to your
returnUrl(if provided) - 5Poll
GET /api/v1/payments?id={id}or listen for the webhook to get final status
const result = await fluxpay.chargePayment({
amount: 99.99,
card: { number: '4111111111111111', holderName: 'JOHN DOE',
expiryMonth: '12', expiryYear: '25', cvv: '123' },
customer: { email: 'john@example.com' }
});
if (result.payment.requires3DS) {
// Redirect customer to 3DS page
res.redirect(result.payment.redirectUrl);
// Then poll for final status
const final = await fluxpay.waitForPayment(result.payment.id);
console.log('Final status:', final.payment.status);
} else if (result.success) {
console.log('Payment completed:', result.payment.id);
} else {
console.log('Payment failed:', result.error);
}Complete Example
const { FluxPayClient } = require('./fluxpay.cjs.js');
// Or ESM: import { FluxPayClient } from './fluxpay.esm.mjs';
const fluxpay = new FluxPayClient({
apiKey: process.env.FLUXPAY_API_KEY, // sk_live_xxx or sk_test_xxx
});
async function chargeCustomer(order) {
try {
const result = await fluxpay.chargePayment({
amount: order.total,
currency: 'USD',
description: `Order #${order.id}`,
card: {
number: order.cardNumber,
holderName: order.cardName,
expiryMonth: order.expMonth,
expiryYear: order.expYear,
cvv: order.cvv,
},
customer: {
email: order.email,
firstName: order.firstName,
lastName: order.lastName,
phone: order.phone,
billing: {
address: order.address,
city: order.city,
state: order.state,
postalCode: order.zip,
country: order.country,
},
},
metadata: { orderId: order.id },
webhookUrl: 'https://yoursite.com/webhooks/fluxpay',
});
if (result.payment.requires3DS) {
// Store payment ID, redirect customer
return { redirect: result.payment.redirectUrl, paymentId: result.payment.id };
}
if (result.success && result.payment.status === 'COMPLETED') {
return { success: true, paymentId: result.payment.id };
}
// PENDING — wait for webhook or poll
if (result.payment.status === 'PENDING') {
const final = await fluxpay.waitForPayment(result.payment.id);
return { success: final.payment.status === 'COMPLETED', paymentId: final.payment.id };
}
return { success: false, error: result.error };
} catch (err) {
console.error('Charge failed:', err.message);
return { success: false, error: err.message };
}
}Test Mode
Use an API key starting with sk_test_ to test without processing real payments. The outcome is driven by the test card number (see the Test Cards table): …0000 completes, …1111 declines, …2222 triggers 3DS, and …3333 stays pending.
curl -X POST https://fluxpay.online/api/v1/payments/charge \
-H "Authorization: Bearer sk_test_your_test_key" \
-H "Content-Type: application/json" \
-d '{
"amount": 1.00,
"card": {
"number": "4111111111111111",
"holderName": "TEST USER",
"expiryMonth": "12",
"expiryYear": "25",
"cvv": "123"
},
"customer": { "email": "test@example.com" }
}'Refunds
POST /api/v1/refunds initiates a full or partial refund on an existing transaction, using the same Bearer sk_live_* / sk_test_* API key as the endpoints above.
Request
curl -X POST https://fluxpay.online/api/v1/refunds \
-H "Authorization: Bearer sk_live_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"transactionId": "cm4h1abc123def456",
"amount": 25.00,
"reason": "Customer requested partial refund"
}'Response (201)
{
"success": true,
"refund": {
"id": "cm4h1ref789ghi012",
"status": "PENDING",
"amount": 25.00,
"currency": "USD",
"type": "PARTIAL",
"createdAt": "2026-03-24T10:05:00.000Z"
}
}transactionIdstringThe FluxPay transaction ID to refund
amountnumberRefund amount. Omit for a full refund; a smaller value issues a partial refund
reasonstringFree-text reason, stored for your records
In test mode, the refund is auto-processed to a final status. In live mode, it is created as PENDING and requires admin approval before funds move. A payment.refunded or payment.partially_refunded webhook is sent once the refund is finalized. GET /api/v1/refunds lists refunds (filterable by status/transactionId, cursor-paginated).
WooCommerce Integration
Accept payments in your WooCommerce store with the official FluxPay plugin. No code required.
Requirements
Installation
Download & Install the Plugin
Download the zip above, then in your WordPress admin go to Plugins > Add New > Upload Plugin. Select the zip file and click Install Now, then Activate.
Enter Your API Key
Go to WooCommerce > Settings > Payments > FluxPay. Enter the API key from your FluxPay Dashboard (Settings > API Keys).
Enable the Gateway
Toggle the Enable/Disable switch to On. Optionally set a custom title and description shown at checkout.
Test a Payment
Place a test order in your store. You will be redirected to the FluxPay payment page, then back to your order confirmation.
Configuration Settings
| Setting | Description | Default |
|---|---|---|
| Enable/Disable | Show FluxPay at checkout | Disabled |
| Title | Payment method name shown to customers | Credit / Debit Card |
| Description | Payment method description at checkout | Pay securely via FluxPay |
| API Key | Your FluxPay API key (sk_live_... or sk_test_...) | -- |
| Test Mode | Use test API key for sandbox transactions | Off |
Payment Flow
1. Customer clicks "Place Order" in WooCommerce checkout
2. Plugin calls FluxPay API to create a payment
3. Customer is redirected to FluxPay hosted payment page
4. Customer enters card details and completes payment
5. FluxPay sends webhook to your site to confirm the result
6. Plugin updates WooCommerce order status automatically
7. Customer is redirected back to your order confirmation page
Webhooks
The plugin automatically registers a webhook endpoint at yoursite.com/wc-api/fluxpay. Configure this webhook URL and its secret in FluxPay before accepting orders. The plugin rejects unsigned webhooks and updates order status (Processing, Failed, or Refunded) based on the event received.
Test Mode
Enable Test Mode in the plugin settings and use a sk_test_... API key. Test payments are processed in sandbox mode and automatically marked as completed. Use test card 4111 1111 1111 0000 for successful test payments.
Troubleshooting
Payment redirects to a blank page
Ensure your API key is correct and your FluxPay account is active. Check WooCommerce > Status > Logs for errors from the fluxpay gateway.
Order status not updating after payment
Verify that your site is accessible over HTTPS and not blocked by a firewall. The webhook URL must be reachable from FluxPay servers.
Plugin not appearing at checkout
Go to WooCommerce > Settings > Payments and make sure FluxPay is enabled. Also confirm your store currency is supported (USD, EUR, GBP, CAD, AUD).
<?php
// Example: Verifying FluxPay webhook in your custom plugin or theme
// (The WooCommerce plugin handles this automatically)
add_action('rest_api_init', function () {
register_rest_route('my-shop/v1', '/fluxpay-webhook', [
'methods' => 'POST',
'callback' => 'handle_fluxpay_webhook',
]);
});
function handle_fluxpay_webhook(WP_REST_Request $request) {
// IMPORTANT: read the raw body BEFORE any JSON parsing.
// The signature is HMAC-SHA256 over the exact bytes that were sent.
// Re-encoding parsed JSON (e.g. wp_json_encode) will NOT match.
$body = file_get_contents('php://input');
$signature = $request->get_header('X-Webhook-Signature');
$event = $request->get_header('X-Webhook-Event'); // e.g. "payment.success"
$secret = get_option('fluxpay_webhook_secret');
$computed = hash_hmac('sha256', $body, $secret);
if (!hash_equals($computed, $signature)) {
return new WP_REST_Response(['error' => 'Invalid signature'], 401);
}
// The body IS the payload — a flat object, no "data" wrapper.
$payload = json_decode($body, true);
$order_id = $payload['metadata']['orderId'] ?? null;
$order = wc_get_order($order_id);
if ($order && $event === 'payment.success' && $payload['status'] === 'COMPLETED') {
$order->payment_complete($payload['transactionId']);
}
return new WP_REST_Response(['success' => true], 200);
}