Skip to content
Fluxer API

Billing

These routes take payment for premium, verify an account holder’s age, and refund the most recent purchase. Checkout and age verification each finish on the payment provider’s own pages, so those routes create the provider session and return 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.

A self-hosted deployment never serves age verification or the refund routes, serves the checkout routes only while billing is active, and serves receive Stripe webhook while Stripe is serviceable, as deployment availability describes. All of them are user-only except receive Stripe webhook.

One absolute URL that completes a billing operation in a browser. Create subscription checkout, 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 Usually 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"
}

Whether the account’s most recent purchase can still be refunded without operator involvement. When no refundable purchase was resolved, eligible and cancels_subscription are false. invoice_id, invoice_amount_paid_cents, currency, paid_at and refund_window_expires_at are null. 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 made 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 when the charge was paid with Pix and the invoice amount is greater than one minor unit

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 with an active in-app purchase subscription returns the same code with the reason existing_subscription and the store in the top-level provider member. Fluxer never converts this refusal.
  • 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 Fluxer converts the purchase into a scheduled billing cycle change, as described below.

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 then schedules a billing cycle change to the submitted cycle at the end of the current period. That is exactly what 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 explicitly accepted
payment_method?4stringEither card, pix or upi (default card)
is_business?booleanWhether to require a billing address for tax invoicing (default false)

1 Read only when Fluxer cannot geolocate the request, because the request-time geolocation country wins. The resolved country is required whenever the price is not in USD or EUR, and the price is validated against the catalogue for that country, so a client cannot buy outside the catalogue of the country it is browsing from

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. A recurring BRL price from the Fluxer price catalogue always checks out with pix, whatever this field says. A catalogue the operator manages has no such rule

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

Checkout collects the required consent, tax information and any promotion code on the provider’s pages.

A converted purchase schedules a billing cycle change instead of opening checkout. It 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/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 explicitly accepted
is_business?booleanWhether to require a billing address for tax invoicing (default false)
payment_method?stringEither card, pix or upi, which this operation ignores

1 Read only when Fluxer cannot geolocate the request, because the request-time geolocation country wins. The resolved country is required whenever the gift price is not 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 returned URL opens a one-off checkout. The gift appears in List current user gifts only after payment is confirmed.

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

Adult age verification is granted only after receive Stripe webhook processes the matching completion event and confirms a credit card. The 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 failed payment-history lookup or an account with no payment history reports no refundable purchase.

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 that the provider reports as failed or cancelled, the account can attempt the refund again.

StatusBodyCondition
200refund objectThe refund was created, or the request repeats an earlier refund of this invoice and returns that 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. The last two 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 current subscription updates the account’s premium state and sends User Update to every account session.

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

POST/v1/stripe/webhookUnauthenticated

Accepts a signed payment provider event for asynchronous processing and returns a webhook received object. The signature header is the credential.

  • A missing signature header returns 400 STRIPE_WEBHOOK_SIGNATURE_MISSING.
  • A deployment with no payment provider client or webhook secret configured then returns 400 STRIPE_WEBHOOK_NOT_AVAILABLE.
  • A signature header that does not verify returns 401 STRIPE_WEBHOOK_SIGNATURE_INVALID.
FieldTypeDescription
Stripe-SignaturestringPayment provider signature computed over the exact raw request body
StatusBodyCondition
200webhook received objectThe event was authenticated and queued
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 queued

The response comes before processing, and a redelivered event is applied once.

Accepted events update payment, subscription, gift, donation and age-verification results. 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.