Skip to content

🤖 Explain with AI

Payments API

The Payments API enables you to create checkout sessions, process payments, manage payment intents, list your transactions, and refund them. Most endpoints require authentication via API Key; the buyer-facing endpoints use the session ID as their credential.

Base URL

https://api.4geeks.io

Authentication

Merchant endpoints require the X-Api-Key header:

X-Api-Key: sk_test_your_api_key_here

The buyer-facing endpoints (/session/{id}, /session/{id}/status, /intent, /subscription) require no key: the unguessable session ID is the credential. Every session is bound to the merchant that created it, and every read is scoped to that merchant.

See Authentication for details.

Amounts and currencies

amount is always expressed in major units ("99.99"), while the amount of a refunded payment is expressed in the currency’s minor unit (cents). The API converts amounts with the currency’s real precision: zero-decimal currencies (JPY, KRW, CLP…) are not multiplied by 100, and three-decimal currencies (KWD, BHD…) keep their third decimal.

currency must be a three-letter ISO-4217 code. return_url must be an http or https URL.


Create Checkout Session

Creates a secure checkout session (Server-to-Server flow). Returns the session ID and the custom checkout URL to redirect the buyer to.

POST /v1/payments/session

Request Body

Field Type Required Description
amount string Yes Total amount to charge in major units, greater than 0 (e.g., 99.99)
currency string Yes ISO-4217 currency code, e.g. USD
return_url string Yes URL to redirect after payment completion
description string No A description of the payment
items array[string] No Name(s) of product(s) or service(s). A comma-separated string is also accepted
mode string No Checkout mode: payment or subscription. Default: payment
price_id string Yes for subscriptions Price ID used to create the subscription
customer_id string Yes for subscriptions Customer ID used to create the subscription

The merchant must have an approved payment account for the session’s mode (test or live). Otherwise the request fails with a 409 instead of charging the platform account.

Request Example

curl -X POST 'https://api.4geeks.io/v1/payments/session' \
  -H 'X-Api-Key: sk_test_your_api_key_here' \
  -H 'Content-Type: application/json' \
  -d '{
    "amount": "99.99",
    "currency": "USD",
    "description": "Premium subscription",
    "items": ["Premium Plan"],
    "return_url": "https://yoursite.com/success"
  }'
import requests

response = requests.post(
    'https://api.4geeks.io/v1/payments/session',
    headers={'X-Api-Key': 'sk_test_your_api_key_here'},
    json={
        'amount': '99.99',
        'currency': 'USD',
        'description': 'Premium subscription',
        'items': ['Premium Plan'],
        'return_url': 'https://yoursite.com/success'
    }
)
print(response.json())
const response = await fetch('https://api.4geeks.io/v1/payments/session', {
    method: 'POST',
    headers: {
        'X-Api-Key': 'sk_test_your_api_key_here',
        'Content-Type': 'application/json'
    },
    body: JSON.stringify({
        amount: '99.99',
        currency: 'USD',
        description: 'Premium subscription',
        items: ['Premium Plan'],
        return_url: 'https://yoursite.com/success'
    })
});
const data = await response.json();
console.log(data);

Response (201 Created)

Field Type Description
session_id string The unique session ID to pass to the frontend
checkout_url string The URL you should redirect your user to

Response Example

{
    "session_id": "sess_1a2b3c4d5e6f",
    "checkout_url": "https://checkout.4geeks.io/checkout?session_id=sess_1a2b3c4d5e6f"
}

Get Checkout Session

Retrieves safe details of a checkout session for the frontend to display. No API key required because the session ID acts as a secure token for the buyer.

GET /v1/payments/session/{session_id}

Path Parameters

Parameter Type Required Description
session_id string Yes The checkout session ID

Request Example

curl -X GET 'https://api.4geeks.io/v1/payments/session/sess_1a2b3c4d5e6f'
import requests

response = requests.get(
    'https://api.4geeks.io/v1/payments/session/sess_1a2b3c4d5e6f'
)
print(response.json())
const response = await fetch('https://api.4geeks.io/v1/payments/session/sess_1a2b3c4d5e6f');
const data = await response.json();
console.log(data);

Response (200 OK)

Field Type Description
id string Session ID
amount string Amount in major units
currency string Currency code
description string Payment description
items array[string] Product/service names
mode string Checkout mode
price_id string Price ID for subscriptions
customer_id string Customer ID
return_url string Redirect URL
status string pending, completed, failed or expired
gateway_account_id string Connected account that receives the funds
merchant_name string Merchant name from the API keys table
is_test boolean Whether the session belongs to the test environment

Response Example

{
    "id": "sess_1a2b3c4d5e6f",
    "amount": "99.99",
    "currency": "USD",
    "description": "Premium subscription",
    "items": ["Premium Plan"],
    "mode": "payment",
    "price_id": null,
    "customer_id": null,
    "return_url": "https://yoursite.com/success",
    "status": "pending",
    "gateway_account_id": "acct_abc123",
    "merchant_name": "My Store",
    "is_test": false
}

Get Checkout Session Status

Minimal status for checkout polling. It returns no payment details and is never cached, so the checkout page can poll it while the buyer confirms the payment.

GET /v1/payments/session/{session_id}/status

Path Parameters

Parameter Type Required Description
session_id string Yes The checkout session ID

Request Example

curl -X GET 'https://api.4geeks.io/v1/payments/session/sess_1a2b3c4d5e6f/status'
import requests

response = requests.get(
    'https://api.4geeks.io/v1/payments/session/sess_1a2b3c4d5e6f/status'
)
print(response.json())
const response = await fetch('https://api.4geeks.io/v1/payments/session/sess_1a2b3c4d5e6f/status');
const data = await response.json();
console.log(data);

Response (200 OK)

Field Type Description
session_id string Session ID
status string pending, completed, failed or expired
is_test boolean Whether the session belongs to the test environment

Response Example

{
    "session_id": "sess_1a2b3c4d5e6f",
    "status": "completed",
    "is_test": false
}

Create Payment Intent

Creates the PaymentIntent a branded checkout confirms, using a predefined session ID. No API key required because the session is already securely bound to a merchant.

The call is idempotent per session: a retry (double click, network retry) returns the same PaymentIntent instead of charging the buyer twice. A session that is no longer pending, or that belongs to a subscription, is rejected with 409.

POST /v1/payments/intent

Request Body

Field Type Required Description
session_id string Yes The secure checkout session ID

Request Example

curl -X POST 'https://api.4geeks.io/v1/payments/intent' \
  -H 'Content-Type: application/json' \
  -d '{
    "session_id": "sess_1a2b3c4d5e6f"
  }'
import requests

response = requests.post(
    'https://api.4geeks.io/v1/payments/intent',
    json={'session_id': 'sess_1a2b3c4d5e6f'}
)
print(response.json())
const response = await fetch('https://api.4geeks.io/v1/payments/intent', {
    method: 'POST',
    headers: {'Content-Type': 'application/json'},
    body: JSON.stringify({session_id: 'sess_1a2b3c4d5e6f'})
});
const data = await response.json();
console.log(data);

Response (201 Created)

Field Type Description
client_secret string PaymentIntent client_secret. Pass this to the client SDK on the frontend
payment_intent_id string The unique PaymentIntent ID
amount string Amount in major units
currency string Currency code

Response Example

{
    "client_secret": "pi_1a2b3c_secret_4d5e6f",
    "payment_intent_id": "pi_1a2b3c4d5e6f",
    "amount": "99.99",
    "currency": "USD"
}

Keep the payment intent ID

Store payment_intent_id: it is the identifier you pass to the refund endpoint when the session was created with this flow.


Create Subscription (via Payment Intent)

Creates a subscription for a secure mode: subscription session and returns the client secret of the first invoice’s PaymentIntent.

POST /v1/payments/subscription

Request Body

Field Type Required Description
session_id string Yes The secure checkout session ID

Request Example

curl -X POST 'https://api.4geeks.io/v1/payments/subscription' \
  -H 'Content-Type: application/json' \
  -d '{
    "session_id": "sess_1a2b3c4d5e6f"
  }'
import requests

response = requests.post(
    'https://api.4geeks.io/v1/payments/subscription',
    json={'session_id': 'sess_1a2b3c4d5e6f'}
)
print(response.json())
const response = await fetch('https://api.4geeks.io/v1/payments/subscription', {
    method: 'POST',
    headers: {'Content-Type': 'application/json'},
    body: JSON.stringify({session_id: 'sess_1a2b3c4d5e6f'})
});
const data = await response.json();
console.log(data);

Response (201 Created)

Same schema as Create Payment Intent.

A subscription whose first invoice needs no payment (for example, a fully discounted period) answers 409 instead of returning an empty client secret.


Create Payment

Creates a hosted checkout session for the legacy Server-to-Server flow.

POST /v1/payments

Request Body

Field Type Required Description
amount string Yes Total amount to charge in major units, greater than 0
description string Yes A description of the payment or service
currency string Yes ISO-4217 currency code, e.g. USD, CAD, CRC, EUR
items array[string] Yes Name(s) of product(s) or service(s)
return_url string Yes URL to redirect after payment completion

Request Example

curl -X POST 'https://api.4geeks.io/v1/payments' \
  -H 'X-Api-Key: sk_test_your_api_key_here' \
  -H 'Content-Type: application/json' \
  -d '{
    "amount": "49.99",
    "description": "Monthly plan",
    "currency": "USD",
    "items": ["Monthly Subscription"],
    "return_url": "https://yoursite.com/thank-you"
  }'
import requests

response = requests.post(
    'https://api.4geeks.io/v1/payments',
    headers={'X-Api-Key': 'sk_test_your_api_key_here'},
    json={
        'amount': '49.99',
        'description': 'Monthly plan',
        'currency': 'USD',
        'items': ['Monthly Subscription'],
        'return_url': 'https://yoursite.com/thank-you'
    }
)
print(response.json())
const response = await fetch('https://api.4geeks.io/v1/payments', {
    method: 'POST',
    headers: {
        'X-Api-Key': 'sk_test_your_api_key_here',
        'Content-Type': 'application/json'
    },
    body: JSON.stringify({
        amount: '49.99',
        description: 'Monthly plan',
        currency: 'USD',
        items: ['Monthly Subscription'],
        return_url: 'https://yoursite.com/thank-you'
    })
});
const data = await response.json();
console.log(data);

Response (202 Accepted)

Field Type Description
code integer HTTP status code (default: 202)
title string Response title (default: “Action required”)
content string Response message (default: “Show the checkout page to complete payment.”)
data object Payment data object
data.id_payment string Payment ID
data.redirect string Redirect URL for checkout

The amount is charged on the merchant’s connected account. The return_url keeps its query string and receives an extra id_payment parameter.

Response Example

{
    "code": 202,
    "title": "Action required",
    "content": "Show the checkout page to complete payment.",
    "data": {
        "id_payment": "pay_7g8h9i0j1k2l",
        "redirect": "https://checkout.4geeks.io/pay/pay_7g8h9i0j1k2l"
    }
}

List Payments

Lists the caller’s checkout sessions, newest first. Results are always scoped to the merchant that owns the API key, and to the environment of the key (test or live).

GET /v1/payments

Query Parameters

Parameter Type Required Default Description
status string No - Filter by pending, completed, failed or expired
limit integer No 10 Page size, 1–100
offset integer No 0 Number of results to skip

Request Example

curl -X GET 'https://api.4geeks.io/v1/payments?status=completed&limit=5' \
  -H 'X-Api-Key: sk_test_your_api_key_here'
import requests

response = requests.get(
    'https://api.4geeks.io/v1/payments',
    headers={'X-Api-Key': 'sk_test_your_api_key_here'},
    params={'status': 'completed', 'limit': 5}
)
print(response.json())
const response = await fetch('https://api.4geeks.io/v1/payments?status=completed&limit=5', {
    headers: {'X-Api-Key': 'sk_test_your_api_key_here'}
});
const data = await response.json();
console.log(data);

Response (200 OK)

Field Type Description
items array[object] Checkout sessions, same schema as Get Checkout Session
total_items integer Total number of sessions
limit integer Requested page size
offset integer Requested offset
has_more boolean Whether another page exists

Response Example

{
    "items": [
        {
            "id": "sess_1a2b3c4d5e6f",
            "amount": "99.99",
            "currency": "USD",
            "description": "Premium subscription",
            "items": ["Premium Plan"],
            "mode": "payment",
            "price_id": null,
            "customer_id": null,
            "return_url": "https://yoursite.com/success",
            "status": "completed",
            "gateway_account_id": "acct_abc123",
            "merchant_name": "My Store",
            "is_test": true
        }
    ],
    "total_items": 1,
    "limit": 5,
    "offset": 0,
    "has_more": false
}

Get Payment

Retrieves a hosted-checkout payment by its gateway ID. The payment must belong to the merchant that owns the API key; anything else answers 404.

GET /v1/payments/{payment_id}

Path Parameters

Parameter Type Required Description
payment_id string Yes The hosted-checkout payment ID

Request Example

curl -X GET 'https://api.4geeks.io/v1/payments/pay_7g8h9i0j1k2l' \
  -H 'X-Api-Key: sk_test_your_api_key_here'
import requests

response = requests.get(
    'https://api.4geeks.io/v1/payments/pay_7g8h9i0j1k2l',
    headers={'X-Api-Key': 'sk_test_your_api_key_here'}
)
print(response.json())
const response = await fetch('https://api.4geeks.io/v1/payments/pay_7g8h9i0j1k2l', {
    headers: {'X-Api-Key': 'sk_test_your_api_key_here'}
});
const data = await response.json();
console.log(data);

Response (200 OK)

Field Type Description
id string The unique payment ID
amount string Amount in major units
currency string Currency code
description string Payment description
status string pending, completed or failed
items array[string] Product/service names
customer_email string Customer email
return_url string Redirect URL, without the internal id_payment parameter
payment_method string Payment method (default: “card”)
card_last4 string Last 4 digits of card
card_brand string Card brand
created_at string (datetime) Creation timestamp
updated_at string (datetime) Update timestamp
test boolean Whether the payment was created with a test key

Response Example

{
    "id": "pay_7g8h9i0j1k2l",
    "amount": "49.99",
    "currency": "USD",
    "description": "Monthly plan",
    "status": "completed",
    "items": ["Monthly Subscription"],
    "customer_email": "customer@example.com",
    "return_url": "https://yoursite.com/thank-you",
    "payment_method": "card",
    "card_last4": "4242",
    "card_brand": "visa",
    "created_at": "2026-04-25T10:30:00Z",
    "updated_at": "2026-04-25T10:31:00Z",
    "test": true
}

Refund a Payment

Refunds are documented separately: see the Refunds API for full and partial refunds, Connect behavior, and refund listings.

POST /v1/payments/{payment_id}/refund

Webhook

Point the gateway’s webhook at this endpoint so the session status stays in sync even if the buyer closes the checkout page before it redirects.

POST /v1/payments/webhook
  • The API verifies the signature of every delivery with the live and test signing secrets (PAYMENT_GATEWAY_WEBHOOK_SECRET, PAYMENT_GATEWAY_TEST_WEBHOOK_SECRET). Unsigned, tampered or replayed deliveries (older than five minutes) are rejected with 400.
  • Payloads are capped at 1 MB.
  • A successful payment marks the session completed; an expired checkout marks it expired; a declined attempt leaves the session pending so the buyer can retry with another card.
  • The update is idempotent: a retried delivery never changes a session twice, and any other event is acknowledged with 200 so the gateway stops retrying.
  • Accounting (balances and transactions) is handled by the platform, not by this endpoint.

No API key is required. Configure the endpoint and its signing secret in the console; requests that are not validly signed are refused.


Errors

Status Meaning
401 Missing or invalid API key
404 Session or payment not found (also when it belongs to another merchant)
409 Business rule: session no longer payable, wrong mode, or the merchant has no approved payment account (gateway_account_not_configured, gateway_account_not_approved, session_not_pending, wrong_session_mode, subscription_no_payment_required)
422 Validation error (amount, currency, URL, mode, missing subscription fields)
429 Rate limit exceeded
502 / 503 The gateway rejected the request or is not configured

Error Response (422 Validation Error)

All endpoints may return a 422 status when validation fails.

{
    "detail": [
        {
            "loc": ["body", "amount"],
            "msg": "field required",
            "type": "value_error.missing"
        }
    ]
}

Business errors use the same envelope as the rest of the API:

{
    "detail": {
        "code": "session_not_pending",
        "message": "This checkout session is no longer payable."
    }
}

Still have questions? Ask on Discord or explore the tutorials.