Payments API
Get paid by your customers from any page. Create a Stripe-hosted Checkout session from your server, manage reusable payment links, or drop a zero-code pay button on your site. Money settles directly into your own connected Stripe account - SoftSolz takes a 0% platform fee.
409.Scopes
All Payments scopes follow the service.payments.* pattern. Declare them on the sk_* key you create in the app's Developers tab.
| Scope | Allows |
|---|---|
service.payments.checkout.create | Create a hosted Checkout session for a one-off payment. |
service.payments.links.read | List the payment links in your workspace. |
service.payments.links.write | Create and update payment links. |
Server API
Server-to-server endpoints live under /api/v1/services/payments/. Requests with a browser Origin header are rejected by design - call these from your backend. A sk_test_* key routes to your paired sandbox tenant. Pass an optional Idempotency-Key header for replay-safe retries.
Create a Checkout session
POST /api/v1/services/payments/checkout-sessions · scope service.payments.checkout.create
Use this when your backend decides the amount at request time, for example a cart total or a custom quote. You create the session, redirect the buyer to Stripe-hosted Checkout, and the buyer returns to your site afterwards.
Request body
| Field | Required | Notes |
|---|---|---|
amount | Yes | Number, major currency units (e.g. 49.99). |
currency | Yes | 3-letter ISO code (e.g. "gbp"). |
description | No | String shown to the buyer on Checkout. |
reference | No | Your order id - echoed back on the webhook. |
customer_email | No | Pre-fills the buyer's email. |
success_url | Yes | http/https URL the buyer returns to on success. |
cancel_url | Yes | http/https URL the buyer returns to on cancel. |
metadata | No | Flat string to string map, up to 40 keys. |
Request
POST /api/v1/services/payments/checkout-sessions HTTP/1.1
Host: app.softsolz.uk
Authorization: Bearer sk_live_…
Content-Type: application/json
Idempotency-Key: 11111111-2222-3333-4444-555555555555
{
"amount": 49.99,
"currency": "gbp",
"description": "Order #1042",
"reference": "1042",
"customer_email": "buyer@example.com",
"success_url": "https://yoursite.com/pay/success",
"cancel_url": "https://yoursite.com/pay/cancel",
"metadata": { "order_id": "1042" }
}
Response 201
{
"data": {
"id": 8123,
"url": "https://checkout.stripe.com/c/pay/cs_live_…",
"stripe_session_id": "cs_live_…",
"amount_cents": 4999,
"currency": "GBP",
"status": "created",
"expires_at": "2026-05-19T18:30:00Z",
"created_at": "2026-05-19T18:00:00Z"
}
}
Redirect the buyer's browser to data.url. Stripe collects the card and settles the funds directly into your connected account.
Errors
| Status | Code | When |
|---|---|---|
409 | CONNECT_ACCOUNT_MISSING | No Stripe Connect account created yet. |
409 | CONNECT_ACCOUNT_NOT_READY | Stripe onboarding incomplete - charges not enabled. |
400 | invalid_amount | Amount missing or not a positive number. |
400 | invalid_currency | Currency not a 3-letter ISO code. |
400 | invalid_url | success_url / cancel_url not http/https. |
403 | insufficient_scope | Key missing service.payments.checkout.create, or a browser origin was sent. |
409 | idempotency_key_conflict | Same Idempotency-Key replayed with a different body. |
Request body fields:
| Field | Type | Required | Description |
|---|---|---|---|
success_url | string (uri) | required | http(s) URL Stripe redirects to after a successful payment. |
cancel_url | string (uri) | required | http(s) URL Stripe redirects to if the customer cancels. |
payment_link_token | string | null | optional | Token of an existing payment button to bill against. When supplied, currency (and a fixed amount, if the button has one) come from the button. Omit to charge an ad-hoc amount (then amount_cents and currency are required). |
amount_cents | integer | conditional | Amount to charge in MINOR units (cents/pence). Required for ad-hoc charges and custom-amount buttons; ignored when the button has a fixed amount. Minimum 1. |
currency | string | conditional | ISO 4217 3-letter currency code. Required for ad-hoc charges; taken from the button when payment_link_token is supplied. |
description | string | null | optional | Line-item name on the Stripe Checkout page (max 250 chars). |
reference | string | null | optional | Free-text reference stored in Stripe metadata (max 500 chars). |
customer_email | string (email) | null | optional | Pre-fills the customer email on the Checkout page. |
Response fields (inside data):
| Field | Type | Description |
|---|---|---|
url | string (uri) | null | Hosted Stripe Checkout URL to redirect the customer to. |
stripe_session_id | string | Stripe Checkout Session id. |
amount_cents | integer | Charged amount in minor units (resolved from the button or the request). |
currency | string | ISO 4217 currency code, upper-cased. |
payment_link_id | integer | null | Id of the payment button used, or null for an ad-hoc charge. |
expires_at | string (date-time) | null | When the Stripe Checkout session expires. |
List payment links
GET /api/v1/services/payments/payment-links · scope service.payments.links.read
Returns the payment links in your workspace, newest first. Supports limit and offset query parameters; the response carries meta.has_more so you can stop paging without an extra round-trip.
Request
GET /api/v1/services/payments/payment-links?limit=50&offset=0 HTTP/1.1
Host: app.softsolz.uk
Authorization: Bearer sk_live_…
Response 200
{
"data": [
{
"id": "lnk_8f3c2a1b9d",
"title": "Studio deposit",
"description": "50% non-refundable booking deposit",
"amount_cents": 5000,
"min_amount_cents": null,
"currency": "GBP",
"amount_editable": false,
"active": true,
"url": "https://app.softsolz.uk/pay/link/lnk_8f3c2a1b9d",
"created_at": "2026-05-12T09:14:00Z"
}
],
"meta": { "limit": 50, "offset": 0, "has_more": false }
}
For a "pay what you want" link, amount_cents is null, amount_editable is true, and min_amount_cents may carry an optional floor.
Create a payment link
POST /api/v1/services/payments/payment-links · scope service.payments.links.write
A payment link is a reusable, shareable page hosted by SoftSolz. Once created, share data.url anywhere, or drop the zero-code button on your site.
Request body
| Field | Required | Notes |
|---|---|---|
title | Yes | Shown to the buyer on the hosted page. |
description | No | Optional supporting text. |
currency | Yes | 3-letter ISO code (e.g. "gbp"). |
amount | Conditional | Fixed price in major units. Omit when amount_editable is true. |
amount_editable | No | true lets the buyer enter the amount. Defaults to false. |
min_amount | No | Optional floor in major units when amount_editable is true. |
show_business_name | No | true shows your workspace name on the button and hosted page. Defaults to false. |
button_color | No | Hex colour for the Pay button background (e.g. "#1d4ed8"). Defaults to a dark button. |
button_text_color | No | Hex colour for the Pay button text (e.g. "#ffffff"). Defaults to white. |
Request
POST /api/v1/services/payments/payment-links HTTP/1.1
Host: app.softsolz.uk
Authorization: Bearer sk_live_…
Content-Type: application/json
Idempotency-Key: 22222222-3333-4444-5555-666666666666
{
"title": "Studio deposit",
"description": "50% non-refundable booking deposit",
"currency": "gbp",
"amount": 50.00,
"amount_editable": false
}
Response 201
{
"data": {
"id": "lnk_8f3c2a1b9d",
"title": "Studio deposit",
"description": "50% non-refundable booking deposit",
"amount_cents": 5000,
"min_amount_cents": null,
"currency": "GBP",
"amount_editable": false,
"active": true,
"url": "https://app.softsolz.uk/pay/link/lnk_8f3c2a1b9d",
"created_at": "2026-05-19T18:00:00Z"
}
}
Request body fields. Amounts are preferably given in MINOR units (cents/pence) via amount_cents / min_amount_cents. The legacy amount / minAmount fields in MAJOR units (pounds/dollars) still work but are deprecated aliases - prefer the *_cents fields.
| Field | Type | Required | Description |
|---|---|---|---|
title | string | required | Button title / Stripe line-item name (min length 1). |
currency | string | required | ISO 4217 3-letter currency code. Stored upper-cased. |
kind | string | optional | Surface type, one of button or link. Any value other than "link" is treated as "button". |
amount_cents | integer | null | optional | Fixed charge in MINOR units (cents/pence) - the preferred, API-consistent field. When set, the button is fixed-amount and the minimum is ignored. |
min_amount_cents | integer | null | optional | Minimum allowed amount in MINOR units for a custom-amount button (used only when amount_cents is omitted). |
amount | number | null | optional (deprecated) | Deprecated legacy alias for amount_cents but in MAJOR units (e.g. pounds/dollars); used only when amount_cents is absent. Prefer amount_cents. |
minAmount | number | null | optional (deprecated) | Deprecated legacy alias for min_amount_cents in MAJOR units (also accepted as min_amount); used only when the *_cents fields are absent. |
description | string | null | optional | Optional longer description (max 2000 chars). |
showBusinessName | boolean | optional | Show the tenant business name on the hosted page. Also accepted as show_business_name. |
buttonColor | string | null | optional | Hex button background colour. Also accepted as button_color. |
buttonTextColor | string | null | optional | Hex button text colour. Also accepted as button_text_color. |
Update a payment link
PATCH /api/v1/services/payments/payment-links/:id · scope service.payments.links.write
Change the title, description, amount, or activation state of an existing link. Send only the fields you want to change. Set active to false to stop the link taking new payments without deleting it.
Request
PATCH /api/v1/services/payments/payment-links/lnk_8f3c2a1b9d HTTP/1.1
Host: app.softsolz.uk
Authorization: Bearer sk_live_…
Content-Type: application/json
{
"title": "Studio deposit (spring season)",
"amount": 75.00,
"active": true
}
Response 200
{
"data": {
"id": "lnk_8f3c2a1b9d",
"title": "Studio deposit (spring season)",
"description": "50% non-refundable booking deposit",
"amount_cents": 7500,
"min_amount_cents": null,
"currency": "GBP",
"amount_editable": false,
"active": true,
"url": "https://app.softsolz.uk/pay/link/lnk_8f3c2a1b9d",
"updated_at": "2026-05-19T18:05:00Z"
}
}
Errors
| Status | Code | When |
|---|---|---|
400 | validation_failed | Body fails validation (e.g. amount on an editable link). |
403 | insufficient_scope | Key missing the required scope, or a browser origin was sent. |
404 | not_found | No payment link with that id in your workspace. |
Pay button & widget
Two ways to take a payment from a public page with no server of your own. Both point at a payment link you created with the Server API or in the SoftSolz app.
Zero-code button
The simplest option. A payment link's url opens the SoftSolz-hosted pay page. Wrap it in a plain anchor and style it however you like - no script, no key.
<a href="https://app.softsolz.uk/pay/link/lnk_8f3c2a1b9d"
target="_blank" rel="noopener">Pay now</a>
Paste this anywhere a link works: a website button, an email, an invoice reminder, or behind a QR code.
Embeddable widget
For an in-page checkout that opens without leaving your site, use the shared loader at https://app.softsolz.uk/softsolz.js with data-service-id="payments".
<script src="https://app.softsolz.uk/softsolz.js"
data-service-id="payments"
data-public-key="pk_live_…"
data-token="lnk_8f3c2a1b9d"></script>
| Attribute | Required | Notes |
|---|---|---|
data-service-id | Yes | Always payments for this widget. |
data-public-key | Yes | A pk_test_* / pk_live_* key with a domain allowlist. |
data-token | Yes | The payment link token the widget should check out. |
The pk_* public key carries a domain allowlist - a request from an origin not on the list is rejected and the widget refuses to load. See Embeds for the loader, modes, and the allowlist rules.
Webhook events
| Event | When |
|---|---|
payments.payment.succeeded | A buyer completes a Checkout session or pays a payment link. |
payments.payment.succeeded data payload:
{
"payment_id": 8123,
"amount_cents": 4999,
"currency": "GBP",
"reference": "1042",
"customer_email": "buyer@example.com",
"stripe_session_id": "cs_live_…",
"stripe_payment_intent_id": "pi_…",
"payment_link_id": "lnk_8f3c2a1b9d"
}
payment_link_id is present only when the payment came from a payment link; it is null for server-created Checkout sessions. The event is delivered to your registered webhook endpoints. See Webhooks for signature verification and retries.