Documentation Index
Fetch the complete documentation index at: https://docs.fluveo.com/llms.txt
Use this file to discover all available pages before exploring further.
Accept payments through the Fluveo API by creating a PaymentIntent, confirming it with a payment method, and handling the resulting status. This guide walks through the full server-side flow.
How it works
Every payment on Fluveo follows three steps:
- Create a PaymentIntent with an amount and currency
- Confirm the PaymentIntent with a payment method
- Handle the result — the payment either succeeds, fails, or requires further action
Step 1: Create a PaymentIntent
A PaymentIntent represents a single payment attempt. Specify the amount in the smallest currency unit (cents for USD) and a three-letter currency code.
curl -X POST https://api.leanrails.com/v1/payment_intents \
-u "sk_test_xxx:" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"amount": 4999,
"currency": "usd",
"description": "Acme Pro Plan — monthly",
"customer": "cus_a1b2c3d4e5",
"metadata": {
"order_ref": "ORD-20260315-001"
}
}'
Response:
{
"id": "pi_7kx9m2nq4r8vw1yz",
"object": "payment_intent",
"amount": 4999,
"amount_received": 0,
"currency": "usd",
"status": "requires_payment_method",
"capture_method": "automatic",
"confirmation_method": "automatic",
"customer": "cus_a1b2c3d4e5",
"description": "Acme Pro Plan — monthly",
"metadata": {
"order_ref": "ORD-20260315-001"
},
"charges": {
"object": "list",
"data": [],
"has_more": false,
"next_cursor": null,
"url": "/v1/charges?payment_intent=pi_7kx9m2nq4r8vw1yz"
},
"client_secret": "pi_7kx9m2nq4r8vw1yz_secret_abc123def456",
"livemode": false,
"created": 1742025600
}
The PaymentIntent starts in requires_payment_method status.
Step 2: Confirm the PaymentIntent
Confirm the PaymentIntent by providing a payment method. In test mode, use one of the test payment methods.
curl -X POST https://api.leanrails.com/v1/payment_intents/pi_7kx9m2nq4r8vw1yz/confirm \
-u "sk_test_xxx:" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"payment_method": "pm_card_visa"
}'
Response:
{
"id": "pi_7kx9m2nq4r8vw1yz",
"object": "payment_intent",
"amount": 4999,
"amount_received": 4999,
"currency": "usd",
"status": "succeeded",
"capture_method": "automatic",
"confirmation_method": "automatic",
"customer": "cus_a1b2c3d4e5",
"payment_method": "pm_card_visa",
"description": "Acme Pro Plan — monthly",
"metadata": {
"order_ref": "ORD-20260315-001"
},
"charges": {
"object": "list",
"data": [
{
"id": "ch_9ab3cd5ef7gh",
"object": "charge",
"amount": 4999,
"amount_refunded": 0,
"currency": "usd",
"status": "succeeded",
"payment_intent": "pi_7kx9m2nq4r8vw1yz",
"payment_method": "pm_card_visa",
"metadata": {},
"created": 1742025605,
"livemode": false
}
],
"has_more": false,
"next_cursor": null,
"url": "/v1/charges?payment_intent=pi_7kx9m2nq4r8vw1yz"
},
"livemode": false,
"created": 1742025600
}
With capture_method: "automatic" (the default), the payment is captured immediately on confirmation.
Manual capture
If you need to authorize a payment now and capture it later (common for marketplaces, hotels, and pre-orders), set capture_method to manual.
Authorize
curl -X POST https://api.leanrails.com/v1/payment_intents \
-u "sk_test_xxx:" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"amount": 15000,
"currency": "usd",
"capture_method": "manual",
"description": "Hotel booking — 2 nights"
}'
After confirming, the PaymentIntent moves to requires_capture instead of succeeded.
Capture
Capture the full amount or a partial amount:
curl -X POST https://api.leanrails.com/v1/payment_intents/pi_7kx9m2nq4r8vw1yz/capture \
-u "sk_test_xxx:" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"amount_to_capture": 15000
}'
Response:
{
"id": "pi_7kx9m2nq4r8vw1yz",
"object": "payment_intent",
"amount": 15000,
"amount_received": 15000,
"currency": "usd",
"status": "succeeded",
"capture_method": "manual",
"livemode": false,
"created": 1742025600
}
Uncaptured PaymentIntents expire after 7 days. Make sure to capture or cancel before the authorization window closes.
Cancel a PaymentIntent
Cancel a PaymentIntent that has not yet succeeded:
curl -X POST https://api.leanrails.com/v1/payment_intents/pi_7kx9m2nq4r8vw1yz/cancel \
-u "sk_test_xxx:" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"cancellation_reason": "requested_by_customer"
}'
Check payment status
Retrieve a PaymentIntent at any time to check its current status:
curl https://api.leanrails.com/v1/payment_intents/pi_7kx9m2nq4r8vw1yz \
-u "sk_test_xxx:"
PaymentIntent statuses
| Status | Meaning |
|---|
requires_payment_method | Created, waiting for a payment method |
requires_confirmation | Has a payment method, waiting for confirmation |
requires_action | Customer action needed (e.g., 3D Secure) |
processing | Payment is being processed |
requires_capture | Authorized, waiting for capture (manual capture only) |
succeeded | Payment completed successfully |
partially_refunded | Partial refund applied, remaining amount still captured |
refunded | Fully refunded (terminal) |
canceled | Payment was canceled |
Listen for payment events
For production integrations, always use webhooks instead of polling. Subscribe to these events:
payment_intent.succeeded — payment completed
payment_intent.payment_failed — payment failed (card declined, etc.)
payment_intent.canceled — payment was canceled
payment_intent.requires_action — customer action needed
Always use idempotency keys on payment creation and confirmation requests to safely retry failed network calls without creating duplicate charges.
Next steps