Backend Setup
Your server is responsible for obtaining an access token from Zatlas and passing it to the frontend. The SDK uses this token for all API calls inside the iframe.
Token endpoint
Section titled “Token endpoint”Your server sends a client_credentials grant to the Zatlas OAuth2 endpoint:
| Environment | URL |
|---|---|
| Sandbox | https://cde-api-staging.zatlas.com/oauth2/token |
| Production | https://cde-api.zatlas.com/oauth2/token |
Server examples
Section titled “Server examples”Node.js (Express)
Section titled “Node.js (Express)”import express from 'express';
const router = express.Router();
router.get('/api/payment-token', async (req, res) => { const response = await fetch(process.env.ZATLAS_TOKEN_URL, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ grant_type: 'client_credentials', client_id: process.env.ZATLAS_CLIENT_ID, client_secret: process.env.ZATLAS_CLIENT_SECRET, }), });
const data = await response.json(); res.json({ accessToken: data.access_token });});
export default router;Python (Flask)
Section titled “Python (Flask)”import osimport requestsfrom flask import Blueprint, jsonify
bp = Blueprint('payment_token', __name__)
@bp.route('/api/payment-token')def get_token(): response = requests.post( os.environ['ZATLAS_TOKEN_URL'], data={ 'grant_type': 'client_credentials', 'client_id': os.environ['ZATLAS_CLIENT_ID'], 'client_secret': os.environ['ZATLAS_CLIENT_SECRET'], }, ) data = response.json() return jsonify(accessToken=data['access_token'])Java (Spring)
Section titled “Java (Spring)”@RestControllerpublic class PaymentTokenController {
@Value("${zatlas.token-url}") private String tokenUrl; @Value("${zatlas.client-id}") private String clientId; @Value("${zatlas.client-secret}") private String clientSecret;
@GetMapping("/api/payment-token") public Map<String, String> getToken() { var client = HttpClient.newHttpClient(); var body = "grant_type=client_credentials" + "&client_id=" + clientId + "&client_secret=" + clientSecret;
var request = HttpRequest.newBuilder() .uri(URI.create(tokenUrl)) .header("Content-Type", "application/x-www-form-urlencoded") .POST(HttpRequest.BodyPublishers.ofString(body)) .build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString()); var json = new ObjectMapper().readTree(response.body());
return Map.of("accessToken", json.get("access_token").asText()); }}Kotlin (Spring WebFlux)
Section titled “Kotlin (Spring WebFlux)”@RestControllerclass PaymentTokenController( @Value("\${zatlas.token-url}") private val tokenUrl: String, @Value("\${zatlas.client-id}") private val clientId: String, @Value("\${zatlas.client-secret}") private val clientSecret: String,) { private val webClient = WebClient.create()
@GetMapping("/api/payment-token") suspend fun getToken(): Map<String, String> { val response = webClient.post() .uri(tokenUrl) .contentType(MediaType.APPLICATION_FORM_URLENCODED) .bodyValue("grant_type=client_credentials&client_id=$clientId&client_secret=$clientSecret") .retrieve() .awaitBody<Map<String, Any>>()
return mapOf("accessToken" to response["access_token"].toString()) }}Environment variables
Section titled “Environment variables”Set these on your server (never in frontend code):
| Variable | Example | Description |
|---|---|---|
ZATLAS_TOKEN_URL | https://cde-api-staging.zatlas.com/oauth2/token | OAuth2 token endpoint |
ZATLAS_CLIENT_ID | 3abc123def456... | OAuth2 client ID |
ZATLAS_CLIENT_SECRET | secret-xyz-789... | OAuth2 client secret |
Token caching
Section titled “Token caching”Access tokens are valid for 1 hour (expires_in: 3600). To avoid unnecessary round-trips, cache the token and refresh it before it expires:
let cached = { token: null, expiresAt: 0 };
export async function getAccessToken() { const now = Date.now();
// Refresh 60 seconds before expiry if (cached.token && cached.expiresAt > now + 60_000) { return cached.token; }
const response = await fetch(process.env.ZATLAS_TOKEN_URL, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ grant_type: 'client_credentials', client_id: process.env.ZATLAS_CLIENT_ID, client_secret: process.env.ZATLAS_CLIENT_SECRET, }), });
const data = await response.json();
cached = { token: data.access_token, expiresAt: now + data.expires_in * 1000, };
return cached.token;}Next steps
Section titled “Next steps”- Checkout Integration — Full payment flow with installments and 3D Secure
- Tokenization Integration — Save cards for scheduled or recurring charges