Skip to content
Fluxer API

Billing

These routes take payment for premium, verify an account holder’s age, and refund the most recent purchase. Each of those finishes on the payment provider’s own pages, so a route here creates the provider session and returns its URL for the browser to open. Receive Stripe webhook takes the signed events the provider sends back. The Premium resource owns entitlement state, mirrored billing data and subscription self-service.

Every route here is hosted-only, as deployment availability describes. Every route except receive Stripe webhook and continue localised card preapproval is user-only.

One absolute URL that completes a billing operation in a browser. Create subscription checkout, create localised card preapproval, create gift checkout and create age verification session all answer with it. The object has no session identifier, so a client sends the browser to the returned value.

FieldTypeDescription
url1stringAbsolute URL that completes the operation in a browser

1 Ordinarily an externally hosted payment provider session URL. Create subscription checkout can answer with the Fluxer premium callback URL instead

{
"url": "https://checkout.example.com/c/pay/cs_test_9f8e7d6c5b4a"
}

The state of one card preapproval flow. A localised recurring price is offered only to a card issued in the matching country, so Fluxer creates the paid session only after a separate setup mode session has proven the card country. status says which of the four variants the object is, and each variant defines its own members.

FieldTypeDescription
statusstringPreapproval status
url1stringPaid checkout URL
reason2stringPreapproval rejection reason
actual_country?3?stringDetected two-letter card issuing country

1 Present and required only when status is ready. It is stable for the lifetime of the continuation token

2 Present and required only when status is rejected

3 Defined only inside the rejected variant, where it is optional. It has a value only for country_mismatch, and is null there when the card reported no issuing country

{
"status": "rejected",
"reason": "country_mismatch",
"actual_country": "PT"
}
ValueDescription
pendingThe setup session has not completed, or a concurrent continuation is already resolving the same token
readyThe card was approved and the paid checkout URL is available
rejectedThe card was refused and the paid checkout cannot continue
expiredThe token is empty, unknown, or has passed its one-day lifetime
ValueDescription
country_mismatchCard issuing country does not match the country recorded on the preapproval session
missing_customerPreapproval session metadata had no payment provider customer
missing_payment_methodCompleted setup intent resolved no payment method
missing_setup_intentCompleted session had no setup intent
payment_method_not_cardApproved payment method is not a card
unknownPreapproval failed without a more specific public reason

The verdict on whether the account’s most recent purchase can still be refunded without operator involvement. When no refundable purchase was resolved, eligible is false, every invoice member is null, and cooldown_expires_at still reports an active cooldown unless the reason is feature_unavailable. A client reads eligible and reason before rendering any amount or timestamp.

FieldTypeDescription
eligiblebooleanWhether the latest purchase can be refunded through refund latest purchase
reason?stringRefund ineligibility reason, or null when the purchase is eligible
invoice_id1?stringPayment provider invoice ID, or null when no refundable purchase was resolved
invoice_amount_paid_cents2?integerInvoice amount paid in the currency minor unit
currency3?stringLowercase provider currency of the invoice
paid_at4?ISO8601 timestampTime the invoice payment settled
refund_window_expires_at5?ISO8601 timestampEnd of the refund window
cooldown_expires_at6?ISO8601 timestampEnd of the account refund cooldown, or null when no cooldown is active
cancels_subscription7booleanWhether refunding the invoice also cancels the subscription that produced it

1 The first invoice among the five most recent invoices of the account’s payment provider customer that is paid, has a positive amount, and resolves a payment intent or a charge

2 The field name states cents, but the value is always the minor unit of the invoice currency

3 Reported as usd when the resolved invoice has no currency of its own

4 Taken from the settlement time of the resolved invoice payment, and from the invoice creation time when that payment has no settlement timestamp

5 The value of paid_at plus exactly three days

6 The account’s first self-service refund time plus exactly 30 days, and null once that instant has passed

7 True when the resolved invoice was produced by a subscription, which the confirmed refund then cancels immediately

{
"eligible": true,
"reason": null,
"invoice_id": "in_1QaBcDeFgHiJkLmN",
"invoice_amount_paid_cents": 499,
"currency": "usd",
"paid_at": "2026-08-29T11:04:12.000Z",
"refund_window_expires_at": "2026-09-01T11:04:12.000Z",
"cooldown_expires_at": null,
"cancels_subscription": true
}
ValueDescription
no_refundable_purchaseThe account has no paid invoice with a positive amount and a resolvable payment reference among the invoices considered
outside_refund_windowThe latest refundable purchase settled more than 3 days ago
cooldown_activeThe account performed a self-service refund less than 30 days ago
feature_unavailableThe deployment is self-hosted, or the payment provider is not configured, so self-service refunds are not offered

The refund that refund latest purchase created against the resolved invoice. It describes the request the provider accepted, so status decides whether money has moved.

FieldTypeDescription
invoice_idstringPayment provider invoice ID that was refunded
payment_intent_id?stringPayment provider payment intent ID, or null when the invoice resolved only a charge
charge_id?stringPayment provider charge ID, or null when the invoice resolved only a payment intent
refund_id?stringPayment provider refund ID of the created refund
refunded_amount_cents1integerAmount refunded so far in the invoice currency minor unit
invoice_amount_paid_centsintegerAmount paid on the refunded invoice in the invoice currency minor unit
currency2stringLowercase provider currency of the invoice
subscription_id3?stringPayment provider subscription that was cancelled along with the refund
status4?stringProvider status of the refund

1 Zero until the provider confirms the refund succeeded. Once confirmed it is the amount the provider accepted, which is one minor unit short of invoice_amount_paid_cents in the Pix case

2 Reported as usd when the resolved invoice has no currency of its own

3 Null while the refund is still pending, and null for a one-off purchase

4 The provider value, such as pending, succeeded or failed. Money moves only once it is succeeded

{
"invoice_id": "in_1QaBcDeFgHiJkLmN",
"payment_intent_id": "pi_3QaBcDeFgHiJkLmN",
"charge_id": null,
"refund_id": "re_3QaBcDeFgHiJkLmN",
"refunded_amount_cents": 499,
"invoice_amount_paid_cents": 499,
"currency": "usd",
"subscription_id": "sub_1QaBcDeFgHiJkLmN",
"status": "succeeded"
}

The acknowledgement receive Stripe webhook returns once it authenticates the event and queues it for asynchronous processing.

FieldTypeDescription
receivedbooleanAlways true after the event is authenticated and accepted for processing
{
"received": true
}
POST/v1/stripe/checkout/subscription

Creates a recurring premium checkout session and returns a redirect URL object.

  • An unknown price returns 400 STRIPE_INVALID_PRODUCT.
  • A gift price, a price outside the resolved country catalogue, and an omitted country code for a price that requires one all return 400 STRIPE_INVALID_PRODUCT_CONFIGURATION.
  • An unclaimed account returns 400 UNCLAIMED_ACCOUNT_CANNOT_MAKE_PURCHASES.
  • An unverified email address returns 403 PURCHASE_EMAIL_VERIFICATION_REQUIRED.
  • The purchase-disabled premium flag returns 403 PREMIUM_PURCHASE_BLOCKED with the reason purchase_disabled.
  • A lifetime Visionary account cannot buy a recurring subscription and returns 403 PREMIUM_PURCHASE_BLOCKED with the reason lifetime.
  • An account whose payment provider customer already holds a subscription in the active, trialing, past_due, unpaid, incomplete or paused state returns the same code with the reason existing_subscription, unless the conversion below applies.

reason is a top-level member of the error response, and an existing_subscription refusal also has the blocking status in the top-level subscription_status member.

Fluxer evaluates the lifetime refusal before the unclaimed account, unverified email and purchase flag refusals, so a lifetime Visionary account submitting a recurring price always receives the reason lifetime whatever its account state.

An account already holding an active or trialing subscription can submit a registered recurring non-gift price for the other billing cycle. Fluxer schedules a billing cycle change to the submitted cycle at the end of the current period, exactly as change subscription billing cycle with effective_at set to period_end does. The response is 200 with url set to the Fluxer premium callback URL.

The conversion requires a stored billing cycle on the account that differs from the submitted price’s cycle. Any other blocking subscription status, no stored billing cycle, and a matching cycle all still produce 403 PREMIUM_PURCHASE_BLOCKED. When scheduling the change fails, the caller receives that failure.

FieldTypeDescription
price_idstringRegistered recurring price ID (1-256 characters)
country_code?1stringTwo-letter country used to select the regional price catalogue (2 characters)
client_geoip_country_code?2stringTwo-letter country the client previously observed for itself (2 characters)
eu_withdrawal_waiver_accepted?3booleanWhether the digital content withdrawal waiver was expressly accepted
pricing_mode?stringEither localized or base (default localized)
payment_method?4stringEither card, pix or upi (default card)
is_business?booleanWhether to require a billing address for tax invoicing (default false)

1 Required whenever the resolved price is not denominated in USD or EUR, and validated against the price catalogue for that country

2 Read to resolve the effective country for the withdrawal waiver, and recorded with the payment

3 Considered only when the effective country is inside the European Union or European Economic Area. The effective country is the request-time geolocation country, then the client-observed country, then the pricing country

4 pix requires a BRL recurring price and upi requires an INR recurring price. Both are rejected for a gift price or a non-recurring price with 400 STRIPE_INVALID_PRODUCT_CONFIGURATION

StatusBodyCondition
200redirect URL objectThe checkout session was created, or a blocked purchase was converted into a scheduled billing cycle change
4005error responseThe price, the account, or the payment provider blocks the checkout
403error responseThe email address is unverified, or the purchase is blocked
404error responseThe authenticated account record no longer exists

5 The price is unknown or not valid for the resolved catalogue, the account is unclaimed, the payment provider is not configured, or the payment provider rejected the request and returns STRIPE_ERROR

The operation creates a payment provider customer when the account has none, records the customer identifier on the account, and reconciles the account’s stored subscription identifier with whatever the provider reports for that customer. Where a blocking subscription can still provision premium, Fluxer repairs the account’s premium type, start, end, cancellation flag, billing cycle and grace deadline before it refuses the request.

It then creates an externally hosted checkout with terms of service consent, the withdrawal waiver text attached to that consent, automatic tax, tax identifier collection and promotion codes. It records a pending payment row with the resolved countries and the waiver decision.

A converted purchase creates no checkout session and no payment row. Fluxer builds or updates the subscription schedule, clears pending cancellation, and sends User Update to every account session.

Premium entitlement otherwise changes only after the matching signed event reaches receive Stripe webhook.

3 requests per minute for each authenticated user, on the stripe:checkout:subscription bucket.

POST/v1/stripe/checkout/subscription/preapproval

Creates a setup mode session that captures and verifies a card before a localised recurring purchase, and returns a redirect URL object.

  • The claimed account, verified email and purchase flag requirements of create subscription checkout apply unchanged, with the same codes.
  • An absent country_code, a pricing_mode of base, and a resolved price that is not a recurring price denominated in a currency other than USD and EUR each return 400 STRIPE_INVALID_PRODUCT_CONFIGURATION.
  • The submitted price is always recurring, so the lifetime block and the existing subscription block both apply and return 403 PREMIUM_PURCHASE_BLOCKED with the reason lifetime or existing_subscription.
FieldTypeDescription
price_idstringRegistered localised recurring price ID (1-256 characters)
country_code1stringTwo-letter country used to select the regional price catalogue (2 characters)
client_geoip_country_code?stringTwo-letter country the client previously observed for itself (2 characters)
eu_withdrawal_waiver_accepted?booleanWhether the digital content withdrawal waiver was expressly accepted
pricing_mode?2stringEither localized or base (default localized)
is_business?booleanWhether to require a billing address for tax invoicing (default false)

1 Declared optional by the shared request schema, and this operation refuses a request that omits it

2 A submitted base returns 400 STRIPE_INVALID_PRODUCT_CONFIGURATION

A submitted payment_method is validated against the enum and then discarded. The setup session is always restricted to cards.

StatusBodyCondition
200redirect URL objectThe setup session was created
4003error responseThe country, the price, the account, or the payment provider blocks the setup session
403error responseThe email address is unverified, or the purchase is blocked
404error responseThe authenticated account record no longer exists

3 The country or pricing mode is not localised, the price is unknown or not valid for the resolved catalogue, the account is unclaimed, the payment provider is not configured, or the payment provider rejected the request and returns STRIPE_ERROR

The operation creates a payment provider customer when necessary and reconciles subscription state exactly as create subscription checkout does, then creates an externally hosted setup session restricted to cards.

It also records a preapproval flow keyed by a freshly generated continuation token, holding the price, country, business flag, waiver decision and observed countries. The flow expires one day after its most recent state change. The token is returned in the session’s success callback URL.

The flow stays pending until receive Stripe webhook processes the matching completion event. It is approved only when the card issuing country equals the pricing country. Otherwise the flow records a preapproval rejection reason, and for country_mismatch the detected country. No entitlement changes.

5 requests per minute for each authenticated user, on the stripe:checkout:subscription:preapproval bucket.

POST/v1/stripe/checkout/subscription/preapproval/continueUnauthenticated

Reports the current state of a preapproval flow and creates the paid checkout session once the card is approved, returning a localised card preapproval result object.

The continuation token is the credential, and no credential supplied on the request selects the flow.

An empty token, an unknown token, and a token whose one-day flow has expired are all reported as expired, so the operation never discloses whether a flow exists.

  • A recorded price that is no longer registered returns 400 STRIPE_INVALID_PRODUCT.
  • A recorded price that no longer belongs to the recorded country catalogue returns 400 STRIPE_INVALID_PRODUCT_CONFIGURATION.
  • The claimed account, verified email, purchase flag, lifetime and existing subscription refusals all apply to the account that opened the flow.

Creating the paid session repeats the complete preparation create subscription checkout performs, against the account, price and country recorded on the flow.

FieldTypeDescription
tokenstringContinuation token issued by create localised card preapproval (1-256 characters)
StatusBodyCondition
200localised card preapproval result objectThe current flow state was returned
4001error responseThe recorded price, the recorded account, or the payment provider blocks the flow
403error responseThe recorded account has an unverified email address, or its purchase is blocked
404error responseThe recorded account record no longer exists

1 The recorded price is unknown or is no longer valid for the recorded country catalogue, the recorded account is unclaimed, the payment provider is not configured, or the payment provider rejected the paid session and returns STRIPE_ERROR

A pending, rejected or expired result changes nothing. For an approved flow, Fluxer attempts to make the approved card the customer’s default invoice payment method, then creates the paid checkout even when that update is unavailable.

It applies the checkout preparation described by create subscription checkout with the approved price and country. The same token then always resolves to the same paid checkout URL. Entitlement changes only after the matching signed event reaches receive Stripe webhook.

30 requests per minute for each client IP address, or for the authenticated user when a credential happens to be supplied, on the stripe:checkout:subscription:preapproval:continue bucket.

POST/v1/stripe/checkout/gift

Creates a one-off premium gift checkout session and returns a redirect URL object.

  • The claimed account, verified email and purchase flag requirements of create subscription checkout apply unchanged, with the same codes.
  • The submitted price is a registered gift price for the resolved country catalogue, so a recurring price returns 400 STRIPE_INVALID_PRODUCT_CONFIGURATION.

Neither the lifetime block nor the existing subscription block applies here, so a lifetime Visionary account can buy gifts.

FieldTypeDescription
price_idstringRegistered gift price ID (1-256 characters)
country_code?1stringTwo-letter country used to select the regional gift price catalogue (2 characters)
client_geoip_country_code?stringTwo-letter country the client previously observed for itself (2 characters)
eu_withdrawal_waiver_accepted?booleanWhether the digital content withdrawal waiver was expressly accepted
pricing_mode?stringEither localized or base (default localized)
is_business?booleanWhether to require a billing address for tax invoicing (default false)

1 Required whenever the resolved gift price is not denominated in USD or EUR

A submitted pix or upi is validated against the enum and then discarded. The session restricts no payment method type, so the provider offers whatever the deployment’s provider account accepts for the resolved currency.

StatusBodyCondition
200redirect URL objectThe checkout session was created
4002error responseThe price, the account, or the payment provider blocks the checkout
403error responseThe email address is unverified, or the purchase is blocked
404error responseThe authenticated account record no longer exists

2 The price is unknown or is not a gift price for the resolved catalogue, the account is unclaimed, the payment provider is not configured, or the payment provider rejected the request and returns STRIPE_ERROR

The operation creates a payment provider customer when necessary, creates an externally hosted one-off checkout with invoice creation enabled, and records a pending payment row for the session. No gift exists yet. The gift is created only after receive Stripe webhook processes the matching paid event, after which it appears in List current user gifts.

3 requests per minute for each authenticated user, on the stripe:checkout:gift bucket.

POST/v1/users/@me/age-verification

Creates a setup mode session that verifies the account holder controls a credit card, and returns a redirect URL object. It takes no request body.

  • A deployment with no configured provider returns 400 STRIPE_PAYMENT_NOT_AVAILABLE before any other check.
  • An account that already has the age-verified adult flag then returns 400 AGE_VERIFICATION_ALREADY_VERIFIED.
StatusBodyCondition
200redirect URL objectThe verification session was created
4001error responseThe account is already age verified, or the provider is not configured or rejected the request
404error responseThe authenticated account record no longer exists

1 A rejected request returns STRIPE_ERROR

The operation creates a payment provider customer when necessary, records the customer identifier on the account, and creates an externally hosted setup session restricted to cards.

Adult age verification is granted only after receive Stripe webhook processes the matching completion event and confirms a credit card. That later grant sets the age-verified adult flag and sends User Update to every session owned by the account.

3 requests per minute for each authenticated user, on the age_verification bucket.

GET/v1/premium/refund-eligibility

Returns the refund eligibility object for the authenticated account.

A deployment with no configured payment provider answers with eligible false and the reason feature_unavailable. Otherwise the operation lists the five most recent invoices for the account’s payment provider customer and selects the first that is paid, has a positive amount, and resolves a payment intent or a charge.

A provider listing failure is logged and treated as no refundable purchase, and the request still succeeds. An account that has never had a payment provider customer provisioned is treated the same way.

StatusBodyCondition
200refund eligibility objectThe eligibility state was returned
404error responseThe authenticated account record no longer exists

30 requests per minute for each authenticated user, on the stripe:refund:eligibility bucket.

POST/v1/premium/refund-latest

Refunds the account’s most recent eligible purchase and returns the refund object.

  • A deployment with no configured payment provider returns 400 STRIPE_PAYMENT_NOT_AVAILABLE.
  • An account with no selectable invoice returns 400 STRIPE_NO_PURCHASE_HISTORY.
  • A purchase that settled more than 3 days ago returns 403 STRIPE_REFUND_OUTSIDE_WINDOW.
  • An account whose 30-day cooldown is still running returns 403 STRIPE_REFUND_COOLDOWN_ACTIVE.

The same invoice selection as get refund eligibility applies.

Repeating the request returns the same provider refund. After a refund the provider reports as failed or cancelled, the account can attempt the refund again.

StatusBodyCondition
200refund objectThe refund was created, or the idempotency key resolved an earlier identical refund
4001error responseThe account has no refundable purchase, or the payment provider cannot refund it
403error responseThe purchase is outside the refund window, or the refund cooldown is active
404error responseThe authenticated account record no longer exists

1 The payment provider is not configured, or the payment method could not be determined or the provider rejected the refund, both of which return STRIPE_ERROR

The operation creates a refund for the invoice amount paid, reduced by one minor unit in the Pix case, with the reason requested_by_customer.

A refund the provider confirms as succeeded in the same call cancels the subscription that produced the invoice and starts the 30-day cooldown from that moment. A refund still reported as pending or as awaiting further action does neither. Receive Stripe webhook cancels the subscription and starts the cooldown when it processes the provider event that confirms the refund.

A confirmed refund that cancels the account’s current subscription clears the stored subscription, billing cycle, cancellation flag and grace deadline, and every account session receives User Update. A cancellation that fails is logged and left for the webhook to reconcile.

3 requests per minute for each authenticated user, on the stripe:refund:latest bucket.

POST/v1/stripe/webhookUnauthenticated

Authenticates a payment provider event against the exact raw request body and enqueues it for asynchronous processing, returning a webhook received object. The signature header is the credential.

  • A missing signature header returns 400 STRIPE_WEBHOOK_SIGNATURE_MISSING.
  • A deployment with no configured payment provider client or webhook secret then returns 400 STRIPE_WEBHOOK_NOT_AVAILABLE.
  • A signature header that does not verify returns 401 STRIPE_WEBHOOK_SIGNATURE_INVALID.

The route verifies the signature with the payment provider library’s default timestamp tolerance.

FieldTypeDescription
Stripe-SignaturestringPayment provider signature computed over the exact raw request body
StatusBodyCondition
200webhook received objectThe event was authenticated and enqueued
400error responseThe signature header is missing, or webhook processing is not configured for this deployment
401error responseThe signature did not verify against the raw body
500error responseThe event could not be enqueued

The response precedes fulfilment, and a redelivered event is applied once.

A recognised event first refreshes the local mirror of its provider object, covering customers, products, prices, checkout sessions, subscriptions, invoices, payment intents, payment methods, charges, refunds and disputes. That mirror is what Get premium state reads.

Later processing can create a purchased gift, grant or extend premium, settle or fail a checkout, update invoice failure state, record or clear the post-cancellation grace deadline, apply subscription changes, and resolve a card preapproval. It also completes a donation and sends its confirmation email, and completes adult age verification. A dispute or fraud warning reverses a gift and emails the redeemer whose entitlement was withdrawn, and a dispute that closes in Fluxer’s favour restores the account and emails that account holder. A refund reverses entitlement and settles the self-service refund.

Each account-addressed message is sent only when the receiving account has an email address. The resulting account changes reach connected sessions as User Update.

300 requests per minute for each client IP address, or for the authenticated user when a credential happens to be supplied, on the stripe:webhook bucket. The bucket is exempt from the global bucket.