Last active
July 17, 2025 14:26
-
-
Save blessingk/491e9b4a55890d8396a5813dff2168db to your computer and use it in GitHub Desktop.
COP API
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
openapi: 3.0.0 | |
info: | |
title: Checkout API | |
version: 1.0.0 | |
description: | | |
API for managing checkout brand configurations and sessions. | |
## Authentication | |
This API uses two authentication methods: | |
- Bearer token authentication for user sessions | |
- API key authentication for service-to-service communication | |
## Rate Limiting | |
- Standard rate limit: 100 requests per minute | |
- Burst rate limit: 200 requests per minute | |
- Rate limit headers are included in all responses | |
## Versioning | |
- Current version: v0.9 | |
- Version deprecation policy: 6 months notice | |
- Version in URL: /api | |
- Version in header: X-API-Version | |
## Response Format | |
All responses follow a standard format: | |
```json | |
{} | |
``` | |
## Error Format | |
All errors follow this format: | |
```json | |
{ | |
"statusCode": 404, | |
"message": "Human readable error message", | |
"timestamp": "2024-03-20T10:00:00Z", | |
"path": "/api/resource", | |
"requestId": "req_123456789", | |
"details": {}, // Optional additional error details | |
"trace": [] // Optional stack trace (only in non-production environments) | |
} | |
``` | |
servers: | |
- url: /api | |
description: API v0.9 (Current) | |
components: | |
securitySchemes: | |
BearerAuth: | |
type: http | |
scheme: bearer | |
bearerFormat: JWT | |
description: JWT token for user authentication | |
ApiKeyAuth: | |
type: apiKey | |
in: header | |
name: X-Api-Key | |
description: API key for service-to-service communication | |
schemas: | |
Country: | |
type: object | |
properties: | |
code: | |
type: string | |
description: Country code (e.g., 'ZA') | |
name: | |
type: string | |
description: Country name (e.g., 'South Africa') | |
Payment: | |
type: object | |
properties: | |
method: | |
type: string | |
description: Payment method name | |
SessionBrandResponse: | |
type: object | |
properties: | |
name: | |
type: string | |
version: | |
type: string | |
isActive: | |
type: boolean | |
brand: | |
type: object | |
theme: | |
type: object | |
steps: | |
type: array | |
items: | |
type: object | |
featureFlags: | |
type: object | |
formSchema: | |
type: array | |
items: | |
type: object | |
session: | |
type: object | |
Brand: | |
type: object | |
properties: | |
id: | |
type: string | |
format: uuid | |
description: Unique identifier for the brand config | |
name: | |
type: string | |
description: Brand name | |
domain: | |
type: string | |
description: Brand domain | |
logo: | |
type: object | |
properties: | |
url: | |
type: string | |
format: uri | |
description: URL to the brand logo | |
alt: | |
type: string | |
description: Alt text for the logo | |
meta: | |
type: object | |
properties: | |
title: | |
type: string | |
description: Page title | |
description: | |
type: string | |
description: Page description | |
links: | |
type: object | |
properties: | |
privacyPolicy: | |
type: string | |
format: uri | |
description: Privacy policy URL | |
termsAndConditions: | |
type: string | |
format: uri | |
description: Terms and conditions URL | |
copyrightStartYear: | |
type: string | |
description: Copyright start year | |
paymentProvider: | |
type: object | |
properties: | |
name: | |
type: string | |
description: Payment provider name | |
currencyCode: | |
type: string | |
description: Currency code (e.g., ZAR) | |
logo: | |
type: object | |
properties: | |
url: | |
type: string | |
format: uri | |
description: Payment provider logo URL | |
alt: | |
type: string | |
description: Alt text for the payment provider logo | |
acceptedPaymentMethodLogos: | |
type: array | |
items: | |
type: object | |
properties: | |
url: | |
type: string | |
format: uri | |
description: Payment method logo URL | |
alt: | |
type: string | |
description: Alt text for the payment method logo | |
support: | |
type: object | |
properties: | |
phoneNumber: | |
type: string | |
description: Support phone number | |
BrandResponse: | |
type: object | |
properties: | |
id: | |
type: integer | |
version: | |
type: string | |
isActive: | |
type: boolean | |
brand: | |
$ref: '#/components/schemas/Brand' | |
theme: | |
type: object | |
featureFlags: | |
type: object | |
steps: | |
type: array | |
items: | |
type: object | |
formSchema: | |
type: array | |
items: | |
type: object | |
orderSuccess: | |
type: object | |
createdAt: | |
type: string | |
format: date-time | |
updatedAt: | |
type: string | |
format: date-time | |
createdBy: | |
type: string | |
updatedBy: | |
type: string | |
Error: | |
type: object | |
properties: | |
statusCode: | |
type: integer | |
description: HTTP status code | |
message: | |
type: string | |
description: Human readable error message | |
timestamp: | |
type: string | |
format: date-time | |
description: When the error occurred | |
path: | |
type: string | |
description: The request path that caused the error | |
requestId: | |
type: string | |
format: uuid | |
description: Unique request identifier | |
details: | |
type: object | |
description: Additional error details | |
trace: | |
type: array | |
items: | |
type: string | |
description: Stack trace (only in non-production environments) | |
PaginatedResponse: | |
type: object | |
properties: | |
data: | |
type: array | |
items: | |
type: object | |
meta: | |
type: object | |
properties: | |
total: | |
type: integer | |
description: Total number of items | |
page: | |
type: integer | |
description: Current page number | |
perPage: | |
type: integer | |
description: Number of items per page | |
totalPages: | |
type: integer | |
description: Total number of pages | |
hasMore: | |
type: boolean | |
description: Whether there are more pages | |
requestId: | |
type: string | |
format: uuid | |
timestamp: | |
type: string | |
format: date-time | |
Product: | |
type: object | |
description: Product details to be added to the cart for the session | |
properties: | |
id: | |
type: string | |
format: uuid | |
description: Unique identifier for the product | |
code: | |
type: string | |
description: Product code | |
feature: | |
type: object | |
properties: | |
icon: | |
type: string | |
format: uri | |
text: | |
type: string | |
tooltipText: | |
type: string | |
discounts: | |
type: array | |
items: | |
type: object | |
properties: | |
amount: | |
type: number | |
format: float | |
code: | |
type: string | |
id: | |
type: string | |
maxValue: | |
type: number | |
format: float | |
name: | |
type: string | |
type: | |
type: string | |
enum: [absolute, percentage] | |
optionalExtras: | |
type: array | |
items: | |
type: object | |
properties: | |
icon: | |
type: string | |
format: uri | |
imageUrl: | |
type: string | |
format: uri | |
maxQuantity: | |
type: integer | |
minQuantity: | |
type: integer | |
name: | |
type: string | |
price: | |
type: object | |
properties: | |
amount: | |
type: number | |
format: float | |
description: | |
type: string | |
discountedAmount: | |
type: number | |
format: float | |
quantity: | |
type: integer | |
tooltipText: | |
type: string | |
total: | |
type: number | |
format: float | |
unitMeasure: | |
type: object | |
properties: | |
plural: | |
type: string | |
singular: | |
type: string | |
Session: | |
type: object | |
required: | |
- code | |
- brandId | |
- brandVersion | |
- status | |
- country | |
- currency | |
- timer | |
- payment | |
- cart | |
- createdAt | |
- updatedAt | |
properties: | |
uuid: | |
type: string | |
code: | |
type: string | |
brandId: | |
type: integer | |
brandVersion: | |
type: string | |
status: | |
type: string | |
enum: [active, completed, cancelled, expired] | |
country: | |
$ref: '#/components/schemas/Country' | |
currency: | |
$ref: '#/components/schemas/Currency' | |
timer: | |
$ref: '#/components/schemas/Timer' | |
payment: | |
$ref: '#/components/schemas/Payment' | |
cart: | |
$ref: '#/components/schemas/Cart' | |
formsData: | |
type: object | |
expiresAt: | |
type: string | |
format: date-time | |
createdAt: | |
type: string | |
format: date-time | |
updatedAt: | |
type: string | |
format: date-time | |
sessionHistory: | |
type: array | |
items: | |
$ref: '#/components/schemas/SessionHistoryEntry' | |
RecalculatePricesRequest: | |
type: object | |
required: | |
- sessionId | |
- cart | |
properties: | |
sessionId: | |
type: string | |
format: uuid | |
description: Session identifier | |
cart: | |
$ref: '#/components/schemas/Cart' | |
RecalculatePricesResponse: | |
type: object | |
properties: | |
data: | |
type: object | |
properties: | |
cart: | |
$ref: '#/components/schemas/Cart' | |
totals: | |
type: object | |
properties: | |
subtotal: | |
type: number | |
format: float | |
discount: | |
type: number | |
format: float | |
total: | |
type: number | |
format: float | |
meta: | |
type: object | |
properties: | |
message: | |
type: string | |
timestamp: | |
type: string | |
format: date-time | |
requestId: | |
type: string | |
format: uuid | |
Currency: | |
type: object | |
required: | |
- name | |
- code | |
- symbol | |
properties: | |
name: | |
type: string | |
example: "United States Dollar" | |
code: | |
type: string | |
example: "USD" | |
symbol: | |
type: string | |
example: "$" | |
Timer: | |
type: object | |
required: | |
- timeLimitInSeconds | |
- sessionUpdateUrl | |
- maximumRetries | |
- remainingTimeInSeconds | |
- warningThresholdInSeconds | |
- extendable | |
- extensionTimeInSeconds | |
properties: | |
timeLimitInSeconds: | |
type: integer | |
example: 1800 | |
sessionUpdateUrl: | |
type: string | |
format: uri | |
example: "https://api.brandname.com/sessions/update" | |
maximumRetries: | |
type: integer | |
example: 0 | |
remainingTimeInSeconds: | |
type: integer | |
example: 1800 | |
warningThresholdInSeconds: | |
type: integer | |
example: 300 | |
extendable: | |
type: boolean | |
example: false | |
extensionTimeInSeconds: | |
type: integer | |
example: 0 | |
Cart: | |
type: object | |
required: | |
- products | |
- priceBreakdown | |
properties: | |
products: | |
type: array | |
items: | |
$ref: '#/components/schemas/CartProduct' | |
promoCode: | |
$ref: '#/components/schemas/PromoCode' | |
priceBreakdown: | |
$ref: '#/components/schemas/PriceBreakdown' | |
CartProduct: | |
type: object | |
required: | |
- id | |
- name | |
- quantity | |
- unitMeasure | |
- total | |
- discounts | |
- optionalExtras | |
- productImage | |
- features | |
- additionalInformation | |
properties: | |
id: | |
type: integer | |
name: | |
type: string | |
quantity: | |
type: integer | |
iconName: | |
type: string | |
maxQuantity: | |
type: integer | |
minQuantity: | |
type: integer | |
unitMeasure: | |
$ref: '#/components/schemas/UnitMeasure' | |
url: | |
type: string | |
productId: | |
type: string | |
optionId: | |
type: string | |
total: | |
type: object | |
properties: | |
price: | |
$ref: '#/components/schemas/CartProductTotal' | |
totals: | |
$ref: '#/components/schemas/CartProductTotal' | |
discounts: | |
type: array | |
items: | |
$ref: '#/components/schemas/ProductDiscount' | |
optionalExtras: | |
type: array | |
items: | |
$ref: '#/components/schemas/OptionalExtra' | |
productImage: | |
$ref: '#/components/schemas/Image' | |
features: | |
type: array | |
items: | |
$ref: '#/components/schemas/ProductFeature' | |
additionalInformation: | |
type: array | |
items: | |
$ref: '#/components/schemas/AdditionalInformation' | |
UnitMeasure: | |
type: object | |
properties: | |
quantitySelection: | |
$ref: '#/components/schemas/UnitMeasureItem' | |
priceBreakdown: | |
$ref: '#/components/schemas/UnitMeasureItem' | |
UnitMeasureItem: | |
type: object | |
properties: | |
singular: | |
type: string | |
plural: | |
type: string | |
ProductTotal: | |
type: object | |
required: | |
- price | |
- totals | |
properties: | |
price: | |
$ref: '#/components/schemas/Price' | |
totals: | |
$ref: '#/components/schemas/Price' | |
Price: | |
type: object | |
required: | |
- amount | |
- discountedAmount | |
- description | |
properties: | |
amount: | |
type: integer | |
example: 99999 | |
discountedAmount: | |
type: integer | |
example: 79999 | |
description: | |
type: string | |
example: "Price per person in cents" | |
OptionalExtra: | |
type: object | |
required: | |
- id | |
- name | |
- quantity | |
- unitMeasure | |
- price | |
- image | |
- isAdjustable | |
- total | |
properties: | |
id: | |
type: integer | |
name: | |
type: string | |
quantity: | |
type: integer | |
unitMeasure: | |
$ref: '#/components/schemas/UnitMeasure' | |
price: | |
$ref: '#/components/schemas/CartProductTotal' | |
image: | |
$ref: '#/components/schemas/Image' | |
tooltipText: | |
type: string | |
showInPriceBreakdown: | |
type: boolean | |
isAdjustable: | |
type: boolean | |
total: | |
type: number | |
PromoCode: | |
type: object | |
required: | |
- code | |
properties: | |
code: | |
type: string | |
example: "SUMMER20" | |
PriceBreakdown: | |
type: object | |
properties: | |
totalSavings: | |
$ref: '#/components/schemas/PriceBreakdownDetail' | |
totalDue: | |
$ref: '#/components/schemas/PriceBreakdownDetail' | |
totalDueNow: | |
$ref: '#/components/schemas/PriceBreakdownDetail' | |
PriceBreakdownDetail: | |
type: object | |
properties: | |
amount: | |
type: number | |
title: | |
type: object | |
properties: | |
text: | |
type: string | |
mobileOnlyText: | |
type: string | |
description: | |
type: string | |
tooltipText: | |
type: string | |
displayInBreakdown: | |
type: boolean | |
SessionHistoryEntry: | |
type: object | |
properties: | |
createAt: | |
type: string | |
action: | |
type: string | |
details: | |
type: object | |
properties: | |
reason: | |
type: string | |
userAgent: | |
type: string | |
ipAddress: | |
type: string | |
CartProductTotal: | |
type: object | |
properties: | |
amount: | |
type: number | |
discountedAmount: | |
type: number | |
description: | |
type: string | |
ProductDiscount: | |
type: object | |
properties: | |
id: | |
type: string | |
code: | |
type: string | |
name: | |
type: string | |
amount: | |
type: number | |
iconName: | |
type: string | |
Image: | |
type: object | |
properties: | |
alt: | |
type: string | |
url: | |
type: string | |
ProductFeature: | |
type: object | |
properties: | |
iconName: | |
type: string | |
text: | |
type: string | |
tooltipText: | |
type: string | |
AdditionalInformation: | |
type: object | |
properties: | |
iconName: | |
type: string | |
title: | |
type: string | |
text: | |
type: string | |
parameters: | |
page: | |
name: page | |
in: query | |
description: Page number for pagination | |
required: false | |
schema: | |
type: integer | |
minimum: 1 | |
default: 1 | |
perPage: | |
name: perPage | |
in: query | |
description: Number of items per page | |
required: false | |
schema: | |
type: integer | |
minimum: 1 | |
maximum: 100 | |
default: 20 | |
sort: | |
name: sort | |
in: query | |
description: Sort field and direction (e.g., createdAt:desc) | |
required: false | |
schema: | |
type: string | |
filter: | |
name: filter | |
in: query | |
description: Filter criteria (e.g., status:active) | |
required: false | |
schema: | |
type: string | |
responses: | |
UnauthorizedError: | |
description: Authentication credentials are missing or invalid | |
content: | |
application/json: | |
schema: | |
$ref: '#/components/schemas/Error' | |
ForbiddenError: | |
description: The authenticated user doesn't have permission to access the resource | |
content: | |
application/json: | |
schema: | |
$ref: '#/components/schemas/Error' | |
NotFoundError: | |
description: The requested resource was not found | |
content: | |
application/json: | |
schema: | |
$ref: '#/components/schemas/Error' | |
ValidationError: | |
description: The request payload is invalid | |
content: | |
application/json: | |
schema: | |
$ref: '#/components/schemas/Error' | |
RateLimitError: | |
description: Too many requests | |
content: | |
application/json: | |
schema: | |
$ref: '#/components/schemas/Error' | |
headers: | |
X-RateLimit-Limit: | |
schema: | |
type: integer | |
description: The maximum number of requests allowed per time window | |
X-RateLimit-Remaining: | |
schema: | |
type: integer | |
description: The number of requests remaining in the current time window | |
X-RateLimit-Reset: | |
schema: | |
type: integer | |
description: The time at which the current rate limit window resets (Unix timestamp) | |
tags: | |
- name: Brand | |
description: Operations for managing brand configurations | |
- name: Health | |
description: Health check and system status endpoints | |
- name: Sessions | |
description: Operations for managing checkout sessions | |
paths: | |
/internal/sessions/{id}: | |
parameters: | |
- name: id | |
in: path | |
required: true | |
schema: | |
type: integer | |
format: number | |
description: Session ID | |
get: | |
tags: | |
- Sessions | |
security: [] | |
summary: Get a session by ID | |
description: Retrieve session details by ID | |
responses: | |
200: | |
description: Session details | |
content: | |
application/json: | |
schema: | |
$ref: '#/components/schemas/Session' | |
404: | |
$ref: '#/components/responses/NotFoundError' | |
put: | |
tags: | |
- Sessions | |
security: [] | |
summary: Update a session | |
description: Update session details | |
requestBody: | |
required: true | |
content: | |
application/json: | |
schema: | |
$ref: '#/components/schemas/Session' | |
responses: | |
200: | |
description: Session updated | |
content: | |
application/json: | |
schema: | |
$ref: '#/components/schemas/Session' | |
400: | |
$ref: '#/components/responses/ValidationError' | |
404: | |
$ref: '#/components/responses/NotFoundError' | |
/internal/sessions/{id}/expire: | |
post: | |
tags: | |
- Sessions | |
security: [] | |
summary: Expire session | |
description: Expire a session and optionally extend it | |
parameters: | |
- name: id | |
in: path | |
required: true | |
description: Session ID | |
schema: | |
type: integer | |
format: number | |
requestBody: | |
required: true | |
content: | |
application/json: | |
schema: | |
type: object | |
properties: | |
extensionTime: | |
type: integer | |
description: Time to extend in seconds | |
minimum: 60 | |
maximum: 1800 | |
responses: | |
200: | |
description: Session expired successfully | |
content: | |
application/json: | |
schema: | |
$ref: '#/components/schemas/Session' | |
headers: | |
X-RateLimit-Limit: | |
schema: | |
type: integer | |
X-RateLimit-Remaining: | |
schema: | |
type: integer | |
X-RateLimit-Reset: | |
schema: | |
type: integer | |
400: | |
description: Session could not be expired | |
content: | |
application/json: | |
schema: | |
$ref: '#/components/schemas/Error' | |
404: | |
$ref: '#/components/responses/NotFoundError' | |
429: | |
$ref: '#/components/responses/RateLimitError' | |
/sessions: | |
post: | |
tags: | |
- Sessions | |
security: | |
- ApiKeyAuth: [] | |
summary: Create a new session | |
description: Create a new checkout session with product details and brand configuration. | |
requestBody: | |
required: true | |
content: | |
application/json: | |
schema: | |
type: object | |
required: | |
- brandId | |
- brandVersion | |
- country | |
- currency | |
- timer | |
- payment | |
- cart | |
properties: | |
brandId: | |
type: integer | |
description: ID of the brand configuration to use for the session | |
brandVersion: | |
type: string | |
description: Version of the brand configuration | |
country: | |
$ref: '#/components/schemas/Country' | |
currency: | |
$ref: '#/components/schemas/Currency' | |
timer: | |
$ref: '#/components/schemas/Timer' | |
payment: | |
$ref: '#/components/schemas/Payment' | |
cart: | |
$ref: '#/components/schemas/Cart' | |
formsData: | |
type: object | |
description: Arbitrary form data for the session | |
expiresAt: | |
type: string | |
format: date-time | |
description: Expiry date/time for the session | |
responses: | |
201: | |
description: Session created successfully | |
content: | |
application/json: | |
schema: | |
$ref: '#/components/schemas/SessionBrandResponse' | |
400: | |
$ref: '#/components/responses/ValidationError' | |
401: | |
$ref: '#/components/responses/UnauthorizedError' | |
/internal/brands/{id}: | |
parameters: | |
- name: id | |
in: path | |
required: true | |
schema: | |
type: integer | |
description: Brand configuration ID | |
get: | |
tags: | |
- Brand | |
security: [] | |
summary: Get a brand configuration by ID | |
description: Retrieve brand configuration details | |
responses: | |
200: | |
description: Brand configuration details | |
content: | |
application/json: | |
schema: | |
$ref: '#/components/schemas/BrandResponse' | |
404: | |
$ref: '#/components/responses/NotFoundError' | |
/internal/health: | |
get: | |
tags: | |
- Health | |
security: [] | |
summary: Health check endpoint | |
description: Check the health status of the API and its dependencies | |
responses: | |
200: | |
description: System is healthy | |
content: | |
application/json: | |
schema: | |
type: object | |
properties: | |
status: | |
type: string | |
enum: [healthy, degraded] | |
description: Current system health status | |
version: | |
type: string | |
description: Current API version | |
timestamp: | |
type: string | |
format: date-time | |
description: Current server time | |
dependencies: | |
type: object | |
properties: | |
database: | |
type: string | |
enum: [up, down] | |
cache: | |
type: string | |
enum: [up, down] | |
paymentGateway: | |
type: string | |
enum: [up, down] | |
headers: | |
X-RateLimit-Limit: | |
schema: | |
type: integer | |
description: Rate limit for health checks | |
X-RateLimit-Remaining: | |
schema: | |
type: integer | |
description: Remaining health check requests | |
/recalculate-prices: | |
post: | |
tags: | |
- Sessions | |
summary: Recalculate prices when user modifies quantities or optional extras | |
description: Recalculate cart totals based on updated quantities and optional extras | |
operationId: recalculatePrices | |
requestBody: | |
required: true | |
content: | |
application/json: | |
schema: | |
type: object | |
required: | |
- currency | |
- cart | |
properties: | |
sessionId: | |
type: string | |
description: Session code (optional) | |
brandId: | |
type: integer | |
description: Brand ID (optional) | |
currency: | |
type: string | |
description: Currency code (e.g., 'USD') | |
cart: | |
type: object | |
required: | |
- products | |
properties: | |
products: | |
type: array | |
items: | |
type: object | |
properties: | |
productId: | |
type: string | |
description: Product identifier | |
optionId: | |
type: string | |
description: Option identifier (if applicable) | |
quantity: | |
type: integer | |
description: Quantity of the product | |
optionalExtras: | |
type: array | |
items: | |
type: object | |
properties: | |
id: | |
type: string | |
description: Optional extra identifier | |
quantity: | |
type: integer | |
description: Quantity of the optional extra | |
promoCode: | |
type: string | |
description: Promo code (optional) | |
responses: | |
200: | |
description: Prices recalculated successfully | |
content: | |
application/json: | |
schema: | |
$ref: '#/components/schemas/RecalculatePricesResponse' | |
400: | |
$ref: '#/components/responses/ValidationError' | |
404: | |
$ref: '#/components/responses/NotFoundError' | |
500: | |
description: Server error during price recalculation | |
content: | |
application/json: | |
schema: | |
$ref: '#/components/schemas/Error' |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment