Skip to content

Payments

All endpoints are prefixed with /v1. Unless otherwise noted, all endpoints require authentication via Authorization: Bearer <access_token> or X-API-Key.


GET /v1/wallet

Returns the authenticated user’s wallet. If the user has no wallet record, a default object with balance: 0 is returned.

Headers:

HeaderValueRequired
AuthorizationBearer <access_token>Yes

Request Body: None

Response 200 OK:

{
"wallet": {
"user_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"balance": 150.00,
"created_at": "2025-06-15T10:30:00.000Z",
"updated_at": "2025-09-22T14:05:00.000Z"
}
}

Error Responses:

StatusBodyCondition
401{ "error": "Not authenticated" }Missing or invalid token

GET /v1/wallet/transactions

Returns a paginated list of wallet transactions in descending order (newest first). Uses cursor-based pagination.

Headers:

HeaderValueRequired
AuthorizationBearer <access_token>Yes

Query Parameters:

ParameterTypeRequiredDefaultConstraintsDescription
cursorstringNoOpaque cursor from a previous response for fetching the next page
limitintegerNo20Max 50Number of transactions to return

Request Body: None

Response 200 OK:

{
"transactions": [
{
"id": "txn_a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"user_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"type": "deposit",
"amount": 50.00,
"status": "completed",
"created_at": "2025-09-22T14:05:00.000Z"
},
{
"id": "txn_f6e5d4c3-b2a1-0987-dcba-0987654321fe",
"user_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"type": "withdrawal",
"amount": -25.00,
"status": "pending",
"created_at": "2025-09-20T09:12:00.000Z"
}
],
"next_cursor": "eyJjcmVhdGVkX2F0IjoiMjAyNS0wOS0yMFQwOToxMjowMC4wMDBaIn0="
}
Response FieldTypeDescription
transactionsarrayArray of transaction objects
transactions[].idstringTransaction UUID
transactions[].user_idstringOwner’s user UUID
transactions[].typestringTransaction type (e.g. "deposit", "withdrawal")
transactions[].amountnumberSigned amount (negative for withdrawals)
transactions[].statusstringStatus: "pending", "completed", "failed"
transactions[].created_atstringISO 8601 timestamp
next_cursorstring | nullCursor for the next page, or null if no more results

Error Responses:

StatusBodyCondition
401{ "error": "Not authenticated" }Missing or invalid token

POST /v1/wallet/withdraw

Initiates a withdrawal from the user’s wallet. Creates a transaction record with type="withdrawal", a negative amount, and status="pending". The balance is not immediately deducted — the actual deduction occurs during fulfillment processing.

Headers:

HeaderValueRequired
AuthorizationBearer <access_token>Yes

Request Body:

FieldTypeRequiredConstraintsDescription
amountnumberYesMust be > 0Amount to withdraw
{
"amount": 25.00
}

Response 201 Created:

{
"transaction": {
"id": "txn_f6e5d4c3-b2a1-0987-dcba-0987654321fe",
"user_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"type": "withdrawal",
"amount": -25.00,
"status": "pending",
"created_at": "2025-09-22T15:30:00.000Z"
}
}

Error Responses:

StatusBodyCondition
400{ "error": "Insufficient balance" }Wallet balance is less than the requested amount
400{ "error": "Amount must be greater than 0" }Invalid or missing amount
401{ "error": "Not authenticated" }Missing or invalid token

POST /v1/wallet/deposit

Creates a Stripe Checkout session for depositing funds into the user’s wallet. The wallet balance is not updated immediately — it is credited when the checkout.session.completed webhook fires (see Stripe Webhooks).

Headers:

HeaderValueRequired
AuthorizationBearer <access_token>Yes

Request Body:

FieldTypeRequiredDefaultDescription
amountnumberYesAmount to deposit (must be > 0)
success_urlstringNohttp://localhost:3000URL to redirect to after successful payment
cancel_urlstringNoURL to redirect to if the user cancels checkout
{
"amount": 50.00,
"success_url": "https://app.example.com/wallet?status=success",
"cancel_url": "https://app.example.com/wallet?status=canceled"
}

Response 200 OK:

{
"checkout_url": "https://checkout.stripe.com/c/pay/cs_live_a1b2c3..."
}
Response FieldTypeDescription
checkout_urlstringStripe-hosted checkout URL. Redirect the user here.

Error Responses:

StatusBodyCondition
400{ "error": "Amount must be greater than 0" }Invalid or missing amount
401{ "error": "Not authenticated" }Missing or invalid token

POST /v1/stripe/checkout

Creates a Stripe Checkout session for purchasing a seller’s product or service. The seller must have a connected Stripe account. A platform fee is calculated from the seller’s platform_fee_percent (or the system-wide default if not set). A pending payment record is created in the database.

Headers:

HeaderValueRequired
AuthorizationBearer <access_token>Yes

Request Body:

FieldTypeRequiredDefaultDescription
seller_idstringYesUUID of the seller
amountnumberYesPayment amount in the specified currency
currencystringNo"usd"Three-letter ISO currency code
descriptionstringYesHuman-readable description of the purchase
app_slugstringYesIdentifier of the app/extension initiating the checkout
reference_idstringYesExternal reference ID (e.g. order ID, contract ID)
reference_typestringYesType of the reference (e.g. "order", "contract")
success_urlstringYesURL to redirect to after successful payment
cancel_urlstringYesURL to redirect to if the user cancels checkout
{
"seller_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"amount": 99.99,
"currency": "usd",
"description": "Premium design template",
"app_slug": "marketplace",
"reference_id": "order_abc123",
"reference_type": "order",
"success_url": "https://app.example.com/orders/abc123?status=success",
"cancel_url": "https://app.example.com/orders/abc123?status=canceled"
}

Response 200 OK:

{
"checkout_url": "https://checkout.stripe.com/c/pay/cs_live_x9y8z7..."
}
Response FieldTypeDescription
checkout_urlstringStripe-hosted checkout URL. Redirect the user here.

Error Responses:

StatusBodyCondition
400{ "error": "Seller does not have a connected Stripe account" }Seller has not completed Stripe Connect onboarding
400{ "error": "Missing required fields" }One or more required fields are absent
401{ "error": "Not authenticated" }Missing or invalid token

POST /v1/stripe/connect/onboard

Initiates or resumes Stripe Connect Express onboarding for the authenticated user. On the first call, a new Stripe Express account is created and a business_accounts record is inserted in the database. On subsequent calls, the existing Stripe account is retrieved and a fresh onboarding link is generated.

Headers:

HeaderValueRequired
AuthorizationBearer <access_token>Yes

Request Body:

FieldTypeRequiredDefaultDescription
return_urlstringNoURL to redirect to after the user completes onboarding
refresh_urlstringNoURL to redirect to if the onboarding link expires
{
"return_url": "https://app.example.com/settings/payments",
"refresh_url": "https://app.example.com/settings/payments?refresh=true"
}

Response 200 OK:

{
"onboarding_url": "https://connect.stripe.com/setup/e/acct_1A2B3C4D5E/onboarding..."
}
Response FieldTypeDescription
onboarding_urlstringStripe-hosted onboarding URL. Redirect the user here.

Error Responses:

StatusBodyCondition
401{ "error": "Not authenticated" }Missing or invalid token

GET /v1/stripe/connect/status

Returns the Stripe Connect status for the authenticated user. If the user has not started onboarding, a default object with connected: false is returned.

Headers:

HeaderValueRequired
AuthorizationBearer <access_token>Yes

Request Body: None

Response 200 OK:

{
"connect": {
"user_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"stripe_account_id": "acct_1A2B3C4D5E",
"stripe_charges_enabled": true,
"stripe_payouts_enabled": true,
"stripe_onboarding_complete": true
}
}
Response FieldTypeDescription
connect.user_idstringUser UUID (only present when connected)
connect.stripe_account_idstringStripe Express account ID
connect.stripe_charges_enabledbooleanWhether the account can accept charges
connect.stripe_payouts_enabledbooleanWhether the account can receive payouts
connect.stripe_onboarding_completebooleanWhether onboarding has been fully completed
connect.connectedbooleanfalse when the user has no Stripe Connect account

Error Responses:

StatusBodyCondition
401{ "error": "Not authenticated" }Missing or invalid token

POST /v1/stripe/connect/dashboard

Generates a Stripe Express dashboard login link for the authenticated user. The user must have an existing connected Stripe account.

Headers:

HeaderValueRequired
AuthorizationBearer <access_token>Yes

Request Body: None

Response 200 OK:

{
"dashboard_url": "https://connect.stripe.com/express/acct_1A2B3C4D5E/login..."
}
Response FieldTypeDescription
dashboard_urlstringStripe Express dashboard URL. Redirect the user here.

Error Responses:

StatusBodyCondition
400{ "error": "No connected Stripe account found" }User has not completed Stripe Connect onboarding
401{ "error": "Not authenticated" }Missing or invalid token

POST /v1/webhook/stripe

Receives and processes Stripe webhook events. This endpoint does not use standard API authentication. Instead, it verifies the request using the stripe-signature header and the configured Stripe webhook signing secret.

Headers:

HeaderValueRequired
stripe-signatureStripe webhook signatureYes
Content-Typeapplication/jsonYes

Handled Event Types:

Triggered when a Stripe Checkout session is successfully completed.

BehaviorDescription
Payment recordUpdates the corresponding payment record status to "completed"
Wallet depositIf the session metadata indicates a wallet deposit: inserts a wallet transaction with type="deposit" and status="completed", then increments the user’s wallet balance
Extension routingIf app_slug is present in the session metadata, routes the event to the appropriate extension handler for further processing

Triggered when a new subscription is created.

BehaviorDescription
Profile updateUpdates the user’s profile with the new subscription_status and subscription_expires_at

Triggered when an existing subscription is modified (e.g. plan change, renewal).

BehaviorDescription
Profile updateUpdates the user’s profile subscription_status and subscription_expires_at to reflect the new state

Triggered when a subscription is canceled or expires.

BehaviorDescription
Profile updateSets subscription_status to "canceled" and subscription_plan to null

Triggered when a connected Stripe account’s details change.

BehaviorDescription
Business account updateUpdates the business_accounts record: stripe_charges_enabled, stripe_payouts_enabled, and stripe_onboarding_complete flags

Response 200 OK:

{
"received": true
}

Error Responses:

StatusBodyCondition
400{ "error": "Webhook signature verification failed" }Invalid or missing stripe-signature header

GET /v1/business

Returns the authenticated user’s business account and subscription details.

Headers:

HeaderValueRequired
AuthorizationBearer <access_token>Yes

Request Body: None

Response 200 OK:

{
"business_account": {
"id": "ba_a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"user_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"stripe_account_id": "acct_1A2B3C4D5E",
"stripe_charges_enabled": true,
"stripe_payouts_enabled": true,
"stripe_onboarding_complete": true,
"platform_fee_percent": 10,
"created_at": "2025-06-15T10:30:00.000Z"
},
"subscription": {
"plan": "pro",
"status": "active",
"expires_at": "2026-06-15T10:30:00.000Z"
}
}

If the user has no business account or subscription, the respective field is null:

{
"business_account": null,
"subscription": null
}
Response FieldTypeDescription
business_accountobject | nullBusiness account record, or null
business_account.stripe_account_idstringStripe Express account ID
business_account.stripe_charges_enabledbooleanWhether charges are enabled
business_account.stripe_payouts_enabledbooleanWhether payouts are enabled
business_account.stripe_onboarding_completebooleanWhether Stripe onboarding is complete
business_account.platform_fee_percentnumberCustom platform fee percentage for this seller
subscriptionobject | nullSubscription details, or null
subscription.planstringPlan name: "starter" or "pro"
subscription.statusstringSubscription status (e.g. "active", "canceled")
subscription.expires_atstringISO 8601 expiration timestamp

Error Responses:

StatusBodyCondition
401{ "error": "Not authenticated" }Missing or invalid token

POST /v1/business/upgrade

Creates a Stripe Checkout session in subscription mode for upgrading the user’s business plan. Redirects the user to Stripe to complete payment.

Headers:

HeaderValueRequired
AuthorizationBearer <access_token>Yes

Request Body:

FieldTypeRequiredDefaultDescription
planstringYesPlan to subscribe to. Must be "starter" or "pro"
success_urlstringNoURL to redirect to after successful subscription
cancel_urlstringNoURL to redirect to if the user cancels checkout

Available Plans:

PlanMonthly PriceStripe Amount
starter$9/mo900 cents
pro$29/mo2900 cents
{
"plan": "pro",
"success_url": "https://app.example.com/business?status=subscribed",
"cancel_url": "https://app.example.com/business?status=canceled"
}

Response 200 OK:

{
"checkout_url": "https://checkout.stripe.com/c/pay/cs_live_sub_a1b2c3..."
}
Response FieldTypeDescription
checkout_urlstringStripe-hosted subscription checkout URL

Error Responses:

StatusBodyCondition
400{ "error": "Invalid plan" }plan is not "starter" or "pro"
401{ "error": "Not authenticated" }Missing or invalid token

GET /v1/business/dashboard

Returns aggregate business statistics for the authenticated user. Sales and revenue are calculated from completed payments where the user is the seller.

Headers:

HeaderValueRequired
AuthorizationBearer <access_token>Yes

Request Body: None

Response 200 OK:

{
"stats": {
"total_sales": 142,
"total_revenue": 8549.75,
"total_products": 12
}
}
Response FieldTypeDescription
stats.total_salesnumberTotal number of completed sales as seller
stats.total_revenuenumberTotal revenue from completed payments (in account currency)
stats.total_productsnumberTotal number of products listed by the user

Error Responses:

StatusBodyCondition
401{ "error": "Not authenticated" }Missing or invalid token