Skip to main content

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:
  1. Create a PaymentIntent with an amount and currency
  2. Confirm the PaymentIntent with a payment method
  3. 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

StatusMeaning
requires_payment_methodCreated, waiting for a payment method
requires_confirmationHas a payment method, waiting for confirmation
requires_actionCustomer action needed (e.g., 3D Secure)
processingPayment is being processed
requires_captureAuthorized, waiting for capture (manual capture only)
succeededPayment completed successfully
partially_refundedPartial refund applied, remaining amount still captured
refundedFully refunded (terminal)
canceledPayment 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