FairePlace Payments API
Checkout sessions, payment status, and payment confirmation.
Checkout sessions, payment status, and payment confirmation.
Every API here is available over the APIs.io API and to AI agents over MCP.
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
find_apisBrowse and filter every API in the catalog.get_api_artifactsOne API's artifacts, grouped by type.get_openapiThe primary OpenAPI for this API.find_similar_apisAPIs that look like this one.apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.resolveTurn a domain, URL or GitHub org into the provider it belongs to.find_cohortsEvery scored population of providers in the catalog.curl "https://apis.io/api/v1/apis/faireplace-com:faireplace-com-payments-api"
curl "https://apis.io/api/v1/apis?limit=25"
Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.
Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.
A second provider on the same verified email joins the account you already have.
openapi: 3.2.0
info:
title: FairePlace Property Management Payments API
description: 'Comprehensive API for managing properties, leases, tenants, and all aspects of real estate management.
Built with Rust and Actix-web following hexagonal architecture principles.
## Authentication
This API uses API keys for authentication with tenant-based isolation.
## Base URL
- Production: `https://api.faireplace.com/api`'
version: 1.0.0
contact:
name: FairePlace API Support
email: api-support@faireplace.com
license:
name: MIT
url: https://opensource.org/licenses/MIT
servers:
- url: https://api.faireplace.com/api
description: Production server
security:
- BearerAuth: []
tags:
- name: Payments
description: Checkout sessions, payment status, and payment confirmation.
paths:
/credits/purchase:
post:
operationId: purchaseCredits
summary: Purchase credits
description: 'Initiates credit purchase via Stripe Checkout Session.
## Purchase Process
```
┌─────────────────────────────────────────────────────────────┐
│ 1. Client calls POST /credits/purchase │
│ with package_id, success_url, cancel_url │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────┐
│ Verify package │
│ - Exists? │
│ - Active? │
│ - stripe_price_id? │
└─────────────────────┘
│ │
VALID │ │ INVALID
▼ ▼
┌──────────────────┐ ┌──────────────────┐
│ Retrieve │ │ Error 404/400 │
│ stripeCustomerId │ │ │
│ from Better │ │ │
│ Auth │ │ │
└──────────────────┘ └──────────────────┘
│
▼
┌──────────────────────────────┐
│ Create Stripe Checkout │
│ Session with: │
│ - customer_id │
│ - price_id │
│ - metadata (tenant_id, │
│ user_id, package_id) │
│ - success_url │
│ - cancel_url │
└──────────────────────────────┘
│
▼
┌──────────────────────────────┐
│ Return checkout_url │
│ │
│ Client redirects to │
│ Stripe Checkout │
└──────────────────────────────┘
│
▼
┌──────────────────────────────┐
│ User pays on Stripe │
│ │
│ Stripe sends webhook │
│ checkout.session.completed │
└──────────────────────────────┘
│
▼
┌──────────────────────────────┐
│ Backend credits the credits │
│ via webhook handler │
└──────────────────────────────┘
```
## Prerequisites
- The user must have the **owner** role (no Stripe account for tenants)
- The user must have a `stripeCustomerId` in Better Auth
- If absent, error 400: "You must have a Stripe account to purchase credits"
- The package must be active and have a `stripe_price_id` configured
## Validations
- ✅ Validation of `package_id` (valid UUID)
- ✅ URL validation (`success_url` and `cancel_url` must be valid URLs)
- ✅ Verification that the package exists and is active
- ✅ Verification that the package has a `stripe_price_id`
- ✅ Retrieval of `stripeCustomerId` from Better Auth
- ✅ Creation of Stripe Checkout Session with complete metadata
## Stripe Metadata
The Stripe Checkout Session includes the following metadata for webhook processing:
```json
{
"tenant_id": "tenant_123",
"user_id": "user_456",
"package_id": "550e8400-e29b-41d4-a716-446655440000"
}
```
This metadata allows the webhook to credit the right user
in the correct tenant after successful payment.
## Error Handling
- **400 Bad Request**: Invalid package, invalid URLs, or no Stripe account
- **401 Unauthorized**: Invalid or expired API key
- **403 Forbidden**: User is not an owner
- **404 Not Found**: Package not found or inactive
- **500 Internal Server Error**: Error creating the Checkout Session
## Usage Example
```bash
curl -X POST https://api.faireplace.com/v1/credits/purchase \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d ''{
"package_id": "550e8400-e29b-41d4-a716-446655440000",
"success_url": "https://app.faireplace.com/credits/success?session_id={CHECKOUT_SESSION_ID}",
"cancel_url": "https://app.faireplace.com/credits/cancel"
}''
```
**Response**:
```json
{
"checkout_url": "https://checkout.stripe.com/c/pay/cs_test_a1b2c3d4e5f6...",
"package_id": "550e8400-e29b-41d4-a716-446655440000",
"package_name": "Pack Starter",
"credits_amount": 1000,
"price_cents": 2990
}
```
The user must then be redirected to `checkout_url` to complete payment.'
tags:
- Payments
security:
- BearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/PurchaseCreditsRequest'
examples:
purchase_starter_pack:
summary: Purchase of the Starter Pack
value:
package_id: 550e8400-e29b-41d4-a716-446655440000
success_url: https://app.faireplace.com/credits/success?session_id={CHECKOUT_SESSION_ID}
cancel_url: https://app.faireplace.com/credits/cancel
responses:
'201':
description: 'Purchase session created successfully.
The user must be redirected to `checkout_url` to complete payment.
Credits will be automatically credited after successful payment via webhook.
'
content:
application/json:
schema:
$ref: '#/components/schemas/PurchaseCreditsResponse'
examples:
success:
summary: Session created successfully
value:
checkout_url: https://checkout.stripe.com/c/pay/cs_test_a1b2c3d4e5f6...
package_id: 550e8400-e29b-41d4-a716-446655440000
package_name: Pack Starter
credits_amount: 1000
price_cents: 2990
'400':
description: 'Invalid request.
**Possible causes**:
- Invalid `package_id` (not a valid UUID)
- Invalid `success_url` or `cancel_url` (not valid URLs)
- User does not have a Stripe account (`stripeCustomerId` missing in Better Auth)
- Package does not have a `stripe_price_id` configured
'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
examples:
invalid_package_id:
summary: Invalid package ID
value:
error:
code: VALIDATION_ERROR
message: 'Invalid package_id: invalid uuid'
status: 400
no_stripe_account:
summary: No Stripe account
value:
error:
code: BUSINESS_ERROR
message: You must have a Stripe account to purchase credits
status: 400
no_stripe_price_id:
summary: Package without Stripe Price ID
value:
error:
code: VALIDATION_ERROR
message: Package has no Stripe price ID
status: 400
'401':
$ref: '#/components/responses/Unauthorized'
'403':
description: 'Access forbidden.
The user does not have the **owner** role required to purchase credits.
Only owners can have a Stripe account and purchase credits.
'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'404':
description: 'Package not found or inactive.
The provided `package_id` does not exist, does not belong to the tenant,
or is inactive (and `active_only=true` by default).
'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'500':
$ref: '#/components/responses/InternalServerError'
components:
schemas:
PurchaseCreditsRequest:
type: object
description: 'Request to initiate a credit purchase.
This request creates a Stripe Checkout Session and returns the payment URL.
Payment is processed by Stripe, and credits are automatically credited
via webhook when payment is confirmed.
'
required:
- package_id
- success_url
- cancel_url
properties:
package_id:
type: string
format: uuid
description: 'Identifier of the credit package to purchase.
The package must be active and have a configured `stripe_price_id`.
'
example: 550e8400-e29b-41d4-a716-446655440000
success_url:
type: string
format: uri
description: 'Redirect URL after successful payment.
The user will be redirected to this URL after completing the payment
on the Stripe Checkout page.
**Recommendations**:
- Include a success parameter to identify the transaction
- Example: `https://app.fairplace.com/credits/success?session_id={CHECKOUT_SESSION_ID}`
'
example: https://app.fairplace.com/credits/success?session_id={CHECKOUT_SESSION_ID}
cancel_url:
type: string
format: uri
description: 'Redirect URL if the user cancels the payment.
The user will be redirected to this URL if they close the Stripe Checkout page
without completing the payment.
'
example: https://app.fairplace.com/credits/cancel
Error:
type: object
description: "The error envelope returned by every authenticated endpoint.\n\nTwo things are worth knowing before you write your error handling:\n\n- **`code` is a symbolic string, not a number.** The numeric HTTP status is\n in `status`. Branch on `code`: it is stable, and show `message`, which\n is written for humans and may be reworded without notice.\n- The public guest-signature endpoints use a **different, flatter shape**\n (`{ \"error_code\": …, \"message\": … }`, no `error` wrapper), because they\n are consumed by a page that has no session. Their responses document it\n inline.\n\nThe `X-Request-Id` response header carries the request identifier: quote it\nwhen reporting a problem. It is a header, not a body field.\n"
required:
- error
properties:
error:
type: object
required:
- code
- message
- status
properties:
code:
type: string
description: Stable symbolic code. Branch on this.
enum:
- NOT_FOUND
- UNAUTHORIZED
- VALIDATION_ERROR
- DATABASE_ERROR
- BUSINESS_ERROR
- STORAGE_ERROR
- CONFIG_ERROR
- FORBIDDEN
- CONFLICT
- PARSE_ERROR
- SERIALIZATION_ERROR
- AUTHENTICATION_ERROR
- AUTHORIZATION_ERROR
- EXTERNAL_SERVICE_ERROR
- RATE_LIMIT_EXCEEDED
- PAYMENT_REQUIRED
- PLAN_LIMIT_REACHED
- GONE
- BAD_REQUEST
- INTERNAL_ERROR
- INTERNAL_SERVER_ERROR
example: NOT_FOUND
message:
type: string
description: 'Human-readable explanation, most often in French. Safe to display,
not safe to parse.
'
example: Lease not found
status:
type: integer
description: The HTTP status code, repeated in the body.
example: 404
details:
type: object
additionalProperties: true
description: 'Extra machine-readable context. Only present on some errors: on
`RATE_LIMIT_EXCEEDED` it carries `retry_after_seconds`.
'
example:
retry_after_seconds: 12
PurchaseCreditsResponse:
type: object
description: 'Response after creating a credit purchase session.
Contains the Stripe Checkout page URL where the user must be redirected
to complete the payment.
'
required:
- checkout_url
- package_id
- package_name
- credits_amount
- price_cents
properties:
checkout_url:
type: string
format: uri
description: 'Stripe Checkout page URL.
**Required action**: Redirect the user to this URL to complete the payment.
This URL is valid for a limited time (typically 24 hours).
If the user does not complete the payment within this period, the session expires.
'
example: https://checkout.stripe.com/c/pay/cs_test_a1b2c3d4e5f6...
package_id:
type: string
format: uuid
description: 'Identifier of the selected package.
'
example: 550e8400-e29b-41d4-a716-446655440000
package_name:
type: string
description: 'Name of the selected package.
'
example: Pack Starter
credits_amount:
type: integer
format: int64
description: 'Number of credits that will be credited after successful payment (in cents).
'
example: 1000
price_cents:
type: integer
format: int64
description: 'Package price in euro cents.
'
example: 2990
responses:
InternalServerError:
description: Internal Server Error
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
Unauthorized:
description: Unauthorized
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
securitySchemes:
BearerAuth:
type: http
scheme: bearer
description: API key with fp_* prefix, passed as Bearer token
WorkerApiKey:
type: apiKey
in: header
name: X-API-Key
WorkerBearer:
type: http
scheme: bearer
bearerFormat: Worker