Apple Pay

Render a native Apple Pay button that handles the complete Apple Pay session, including the payment sheet, Touch ID/Face ID, and confirmation.

How it works

1 Create session (server)
2 Initialise SDK (client)
3 Mount Apple Pay button
4 Customer authenticates
Native Apple Pay UI and sheet
Biometric authentication (Touch ID / Face ID)
One-tap checkout experience
Safari + compatible browsers
Payment details sourced from session token
Works with HTML/JS and React

Before getting started

1. Get merchant credentials

As a partner you take payments on behalf of the merchants you serve. Each merchant's Client ID and Client Secret are delivered to your registrationWebhook when the merchant connects Teya to your platform, along with the store_id and payment_webhook_public_key.

Use client_credentials grant to obtain access tokens
Each merchant store has its own set of credentials
Store credentials securely and never expose them in client-side code
A separate Client ID and Client Secret per merchant
Unlike POSLink credentials, these aren't shared across your merchant base. Each merchant you serve gets its own Client ID and Client Secret, delivered to your registrationWebhook when they connect - keep each merchant's credentials separate and don't reuse one set across multiple merchants.
Not registered yet?
Set up your partner account and implement the merchant registration flow first. Teya sends each merchant's credentials to your registration webhook.

2. API URLs

ServiceStagingProduction
Online Payments APIhttps://api.teya.xyzhttps://api.teya.com
OAuthhttps://id.teya.xyz/oauth/v2/oauth-tokenhttps://id.teya.com/oauth/v2/oauth-token
Business Portalhttps://business.teya.xyzhttps://business.teya.com

3. Required scopes

Request these scopes when obtaining your access token:

ScopeDescription
transactions/online/createProcess an online transaction
transactions/online/id/getGet online transaction details
checkout/sessions/createCreate a session for the SDK

Domain Verification

Apple Pay requires that your domain is verified before payments can be processed. Follow these steps:

1 Download the domain verification file from the Business Portal under Integrations > Apple Pay
2 Host the file at https://yourdomain.com/.well-known/apple-developer-merchantid-domain-association
3 Click Verify in the Business Portal to complete domain verification
Domain Verification Required
The Apple Pay button will not appear until your domain is verified. Verification must be completed for each domain (including staging and production).
Without domain verification
If your domain has not been verified, the Apple Pay button will simply not appear in the checkout. There is no error message - the SDK silently hides Apple Pay when verification is incomplete. This applies to both the standalone Apple Pay element and Apple Pay within the Checkout element.

Install the SDK

npm install @teyaproduct/teya-blocks-js

For React applications:

npm install @teyaproduct/teya-blocks-js @teyaproduct/teya-blocks-react

See SDK Initialization for initTeyaBlocks() options, including developmentMode for staging.

  • Customize - Button styles, types, and locale options
  • Card Element - Single unified card input
  • Split Card - Separate card fields
  • Webhooks - Receive payment confirmation notifications
  • Testing - Test cards and integration checklist

Try it - Apple Pay

Follow the interactive quickstart below to build a working integration with the Apple Pay.

0 / 0 steps
server.js — staging
import express from 'express';

const app = express();
let cachedToken = null;
let tokenExpiry = 0;

async function getAccessToken() {
  if (cachedToken && Date.now() < tokenExpiry) return cachedToken;

  const res = await fetch('https://id.teya.xyz/oauth/v2/oauth-token', {
    method: 'POST',
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    body: new URLSearchParams({
      grant_type: 'client_credentials',
      client_id: process.env.TEYA_CLIENT_ID,
      client_secret: process.env.TEYA_CLIENT_SECRET,
      scope: 'checkout/sessions/create',
    }),
  });

  const data = await res.json();
  cachedToken = data.access_token;
  tokenExpiry = Date.now() + (data.expires_in - 60) * 1000;
  return cachedToken;
}

app.get('/api/session', async (req, res) => {
  const token = await getAccessToken();
  const sessionRes = await fetch(
    'https://api.teya.xyz/v2/checkout/sessions',
    {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        Authorization: `Bearer ${token}`,
        'Idempotency-Key': crypto.randomUUID(),
      },
      body: JSON.stringify({
        store_id: process.env.TEYA_STORE_ID,
        amount: { currency: 'EUR', value: 1000 },
        type: 'SALE',
      }),
    }
  );

  const session = await sessionRes.json();
  res.json(session);
});

app.listen(3000, () => console.log('Server running on :3000'));
index.html — staging
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Payment</title>
  <style>
    body { font-family: sans-serif; max-width: 480px; margin: 40px auto; padding: 0 20px; }
    #apple-pay-container { margin: 20px 0; }
    #pay-button { width: 100%; padding: 12px; background: #008BB9; color: #fff; border: none; border-radius: 6px; font-size: 16px; cursor: pointer; }
  </style>
</head>
<body>
  <h1>Complete your payment</h1>
  <div id="apple-pay-container"></div>
  <script type="module" src="app.js"></script>
</body>
</html>
app.js — staging
import { initTeyaBlocks } from '@teyaproduct/teya-blocks-js';

// Fetch session token from your backend
const res = await fetch('/api/session');
const { session_token } = await res.json();

// Initialize the SDK
const teya = await initTeyaBlocks(session_token, {
  locale: 'en-GB',
  appearance: {
    theme: 'default',

  },
  // Routes SDK calls to Teya staging. Remove for production.
  developmentMode: true,
});

// Create and mount the element
const applePay = teya.elements.create('applePay', {
  onReady: () => console.log('Apple Pay ready'),
  onSuccess: (result) => {
    console.log('Apple Pay complete:', result);
  },
  onError: (error) => console.error('Apple Pay error:', error),
});

applePay.mount('#apple-pay-container');