Skip to content

Server-to-Server (S2S)

The S2S integration allows you to send card details directly to TurnStay’s API instead of redirecting to the hosted payment page. It uses a segregated Secure Card Service (SCS) environment for handling card data.

S2S must be enabled for your account. Contact TurnStay to enable this functionality. S2S is currently available for Virtual Card payments.

Hosted CheckoutS2S
Card dataCustomer enters on TurnStay’s pageYou send card details via API
PCI scopeMinimal — you never see card dataHigher — you handle card data server-side
Use caseCustomer-facing checkoutVirtual cards, automated payments, MOTO
3DSHandled automaticallySCS returns next steps if required
SetupAvailable by defaultMust be enabled per account
  1. Create a Payment Intent on TurnStay with payment_type: "Virtual Card". The response includes a next_actions array with SCS URLs.
  2. Complete the payment via the SCS link — either embed the form (lower PCI) or call the SCS confirm API with card details (full S2S).
POST /api/v1/payments/intent

Same endpoint as the hosted checkout, but set payment_type to "Virtual Card".

import requests
resp = requests.post(
"https://staging.turnstay.com/api/v1/payments/intent",
headers={"Authorization": "Bearer sk_test_YOUR_KEY"},
json={
"account_id": YOUR_ACCOUNT_ID,
"billing_amount": 10000,
"billing_currency": "ZAR",
"checkin_date": "2026-12-12",
"customer_phone_number": "+27123456789",
"customer_email": "test@example.com",
"payment_type": "Virtual Card",
},
)
data = resp.json()
action_url = data["next_actions"][0]["url"]

The response includes a next_actions array:

{
"lookup_id": "pi_abc123def456",
"status": { "name": "INITIALIZED" },
"next_actions": [
{
"type": "Tokenize Card",
"url": "https://secure-card-service.pci-staging.turnstay.com/api/v1/card/tokenize?..."
},
{
"type": "Confirm Payment",
"url": "https://staging.turnstay.com/api/v1/payments/intent/pay?..."
}
]
}

Embed the SCS form URL in an iframe or redirect the customer. This is the lowest PCI scope option — SCS handles tokenization, 3DS, and provider interactions.

print(f"Redirect customer to: {action_url}")

If you need to send card details server-side, POST to the SCS confirm endpoint:

POST {SCS_URL}/api/v1/public/payment/intent/{scs_intent_id}/confirm
FieldTypeDescription
cardholder_namestringName on card
card_numberstringFull card number
expiry_monthint1–12
expiry_yearint4-digit year
cvcstring3 or 4 digit CVC
customer_emailstringCustomer’s email
customer_phone_numberstringInternational format
from urllib.parse import urlparse, parse_qs
parsed = urlparse(action_url)
scs_intent_id = parsed.path.rsplit('/', 1)[-1]
client_secret = parse_qs(parsed.query).get('client_secret', [None])[0]
SCS_URL = "https://secure-card-service.pci-staging.turnstay.com"
confirm_url = f"{SCS_URL}/api/v1/public/payment/intent/{scs_intent_id}/confirm"
result = requests.post(
confirm_url,
json={
"cardholder_name": "John Doe",
"card_number": "4242424242424242",
"expiry_month": 12,
"expiry_year": 2026,
"cvc": "123",
"customer_email": "john.doe@example.com",
"customer_phone_number": "+27123456789",
},
params={"client_secret": client_secret} if client_secret else {},
)
  • Prefer Option A (embed/redirect) to minimize your PCI scope.
  • Only use Option B (full S2S) if your infrastructure is PCI-compliant for handling card data.
  • Never log or store raw card numbers.
  • Keep Bearer tokens secret and rotate regularly.