Building an advanced Kayana integration in PHP
Last updated Jan 9, 2026
Build an advanced Kayana integration in PHP form to securely collect card details and accept payments using Kayana's Payment API, powered by RYFT, for your e-commerce store.
Overview
The Kayana Payment API enables you to accept payments from customers without needing to build your own checkout process. This guide walks you through integrating the payment flow in sandbox mode, where you can safely test without processing real transactions.
What you'll build:
- Create a payment instance on your server
- Retrieve the PSP public key for frontend initialisation
- Display a secure payment form using the RYFT SDK
- Process the payment securely
Before you begin
You'll need:
- A Kayana property ID
- Access to the sandbox environment
- Basic knowledge of server-side development (PHP examples provided)
Sandbox vs Production:
- Sandbox: Test environment with fake transactions – no authentication required
- Production: Live environment – requires an authorisation token from Kayana
How it works
The payment flow consists of three steps:
- Server-side: Create a payment instance and receive a client_secret
- Server-side: Fetch the PSP public key for frontend initialisation
- Client-side: Render the payment form using the RYFT SDK and complete the transaction
Set up the server
Base URL
Sandbox:
https://integration.dev.kayana.co.uk
Production:
https://integration.kayana.co.uk
Authentication
All production API requests require an Authorisation header with your API token.
x-api-key: YOUR_API_KEY
Note: In sandbox mode, authentication is optional but can be included for testing production-compatible requests.
Step 1: Create a payment
Create a payment instance by calling the /admin/payment endpoint. This returns a client_secret that you'll use to complete the payment on the frontend.
Endpoint
POST /web/payment
Headers
| Header | Value | Required |
| Content-Type | application/json | Yes |
| x-api-key | YOUR_API_KEY | Yes |
Request Parameters
| Parameter | Type | Description |
| property_id | string | Format Prop_* |
| psp_code | string | RYFT |
| pre_authorise | boolean | Indicates whether the payment should be pre-authorised instead of captured immediately |
| currency_code | string | GBP, EUR, USD |
| amount | number | Decimal amount (example: 12.0) |
| customer_email | string | Customer email |
Example request
<?php $curl = curl_init(); curl_setopt_array($curl, array( CURLOPT_URL => 'https://integration.dev.kayana.co.uk/web/payment', CURLOPT_RETURNTRANSFER => true, CURLOPT_CUSTOMREQUEST => 'POST', CURLOPT_POSTFIELDS => json_encode([ "property_id" => "Prop_abc123example", 'psp_code' => $config['stripe']['psp_code'], "currency" => "GBP", "amount" => 12.0, "customer_email" => "customer@example.com" ]), CURLOPT_HTTPHEADER => array( 'Content-Type: application/json', 'x-api-key: YOUR_API_KEY' ), )); $response = curl_exec($curl); curl_close($curl); $data = json_decode($response, true); echo $response; ?>
Response
{ "status": true, "data": { "instance_id": "Payment_xyz789example", "client_secret": "ps_secret123example", "next_step": { "action": "FORM", "proceed": true }, "transaction_id": "Tx_txn456example" } }
Response parameters
| Parameter | Description |
| instance_id | Unique identifier for this payment instance |
| client_secret | Secret key used to complete the payment on the frontend (keep this secure) |
| next_step.action | Indicates the next action required (typically FORM for card payments) |
| next_step.proceed | Boolean indicating whether to proceed with the payment flow |
| transaction_id | Unique identifier for tracking this transaction |
Important: The client_secret is a sensitive piece of information and should be kept secure. Only pass it to your frontend when needed to complete the payment.
Step 2: Retrieve the PSP public key
Fetch the public key required to initialise the RYFT payment SDK on your frontend. This key is specific to your PSP (RYFT) and the currency associated with it.
Endpoint
GET /web/payment/public-key
Headers
| Header | Value | Required |
| x-api-key | YOUR_API_KEY | Yes |
| x-psp-code | RYFT | Yes |
| x-currency-code | GBP (or your currency) | Yes |
Example request
<?php $curl = curl_init(); curl_setopt_array($curl, array( CURLOPT_URL => 'https://integration.dev.kayana.co.uk/web/payment/public-key', CURLOPT_RETURNTRANSFER => true, CURLOPT_CUSTOMREQUEST => 'GET', CURLOPT_HTTPHEADER => array( 'x-api-key: YOUR_API_KEY', 'x-psp-code: RYFT', 'x-currency-code: GBP' ), )); $response = curl_exec($curl); curl_close($curl); $data = json_decode($response, true); echo $response; ?>
Response
{ "status": true, "data": { "public_key": "pk_sandbox_abc123examplekey" } }
Response parameters
| Parameter | Description |
| public_key | PSP public key for initialising the RYFT SDK on your frontend |
Step 3: Complete the payment on the frontend
Use the public key to initialise the RYFT SDK and the client secret to confirm the payment.
Integration overview
- Include the RYFT SDK in your frontend
- Initialise RYFT with your public_key
- Create a payment form using RYFT's UI components
- Confirm the payment using the client_secret
Note: The exact frontend implementation depends on the RYFT JavaScript SDK. Refer to RYFT's documentation for detailed frontend integration steps.
Basic frontend flow
// Initialize RYFT with the public key const ryft = Ryft('pk_sandbox_abc123examplekey'); // Create a payment form const elements = ryft.elements(); const cardElement = elements.create('card'); cardElement.mount('#card-element'); // Handle form submission const form = document.getElementById('payment-form'); form.addEventListener('submit', async (event) => { event.preventDefault(); // Confirm payment with client_secret const {error, paymentIntent} = await ryft.confirmCardPayment( 'ps_secret123example', // client_secret from Step 1 { payment_method: { card: cardElement } } ); if (error) { console.error(error.message); } else { console.log('Payment successful!', paymentIntent); } });
Testing in sandbox
Use these test card numbers to simulate different payment scenarios in the sandbox environment. These cards do not process real transactions.
Test card numbers
| Card Number | Brand | Scenario |
| 4242 4242 4242 4242 | Visa | Successful payment |
| 4000 0025 0000 3155 | Visa | Requires authentication (3D Secure) |
| 4000 0000 0000 9995 | Visa | Payment declined (insufficient funds) |
Additional test card details:
- Expiry date: Any future date (e.g., 12/34)
- CVC: Any 3 digits (e.g., 123)
- Postal code: Any valid format (e.g., SW1A 1AA)
Warning: Sandbox test cards must never be used in production. Real card details should only be entered in the production environment.
Additional endpoints for orders
Suggested flow: Create Order → Authorise Payment
↓
├── Capture → Success
├── Cancel → Void payment
└── Refund → After capture
Authentication
All API requests must include the following headers:
| Header | Type | Required | Description |
|---|---|---|---|
| x-api-key | String | Yes | Your API key |
| type | String | Yes | Must be set to business |
| Content-Type | String | Yes | Must be application/json |
1. Capture Authorised Payment
Endpoint
POST /web/payment/capture-authorised-payment
Request Parameters
| Field | Type | Required | Description |
|---|---|---|---|
| order_id | String | Yes | Order ID |
| amount | Number | Yes | Amount to capture |
cURL Example
curl --location 'https://integration.dev.kayana.co.uk/web/payment/capture-authorised-payment' \ --header 'Content-Type: application/json' \ --header 'type: business' \ --header 'x-api-key: <YOUR_API_KEY>' \ --data '{ "order_id": "Order_xxx", "amount": 5 }'
Sample Response
{ "status": "success", "message": "Payment captured successfully", "data": { "order_id": "Order_xxx", "captured_amount": 5 } }
2. Cancel Authorised Payment
Endpoint
POST /web/payment/cancel-authorised-payment
Request Parameters
| Field | Type | Required | Description |
|---|---|---|---|
| order_id | String | Yes | Order ID |
cURL Example
curl --location 'https://integration.dev.kayana.co.uk/web/payment/cancel-authorised-payment?order_id=Order_xxx' \ --header 'Content-Type: application/json' \ --header 'type: business' \ --header 'x-api-key: <YOUR_API_KEY>'
Sample Response
{ "status": "success", "message": "Payment cancelled successfully" }
3. Refund Payment
Endpoint
POST /web/payment/refund
Request Parameters
| Field | Type | Required | Description |
|---|---|---|---|
| order_id | String | Yes | Order ID |
| amount | Number | Yes | Refund amount |
cURL Example
curl --location --request POST 'https://integration.dev.kayana.co.uk/web/payment/refund?order_id=Order_xxx&amount=0.4' \ --header 'Content-Type: application/json' \ --header 'type: business' \ --header 'x-api-key: <YOUR_API_KEY>'
Sample Response
{ "status": "success", "message": "Refund processed", "data": { "order_id": "Order_xxx", "refunded_amount": 0.4 } }
4. Fetch Order Details
Endpoint
GET /web/order/fetch-order-details-by-order-id
Request Parameters
| Field | Type | Required | Description |
|---|---|---|---|
| order_id | String | Yes | Order ID |
cURL Example
curl --location 'https://integration.dev.kayana.co.uk/web/order/fetch-order-details-by-order-id?order_id=Order_xxx' \ --header 'type: business' \ --header 'x-api-key: <YOUR_API_KEY>'
Sample Response
{ "status": "success", "data": { "order_id": "Order_xxx", "status": "captured", "amount": 5 } }
Error handling
All API responses include a status field indicating success or failure.
Error response format
{ "status": false, "message": "Description of what went wrong" }
Common errors
| Error | Cause | Solution |
| Invalid authorization token | Token is missing, expired, or incorrect | Verify your authorisation token with Kayana support |
| Missing required headers | Required header (e.g., x-psp-code) not provided | Check that all the necessary headers are included in your request |
| Invalid property ID | Property ID format is incorrect or doesn't exist | Verify your property ID matches the format Prop_* |
| Unsupported currency | Currency code is not supported for this property | Check supported currencies with Kayana support |
| PSP configuration missing | RYFT is not configured for this property | Contact Kayana to enable RYFT for your account |
Going live
Before moving to production:
- Get production credentials: Contact Kayana Technical Support for your production authorisation token
- Update your base URL: Switch from the sandbox URL to the production URL
- Add authentication: Include your authorisation token in all requests
- Update frontend keys: Replace sandbox public keys with production keys
- Remove test cards: Ensure only real payment methods are accepted
Necessary: Never use sandbox keys or test cards in a production environment. Production keys must be kept secure and never exposed in client-side code.
Support
Need help?
- Technical issues: Contact Kayana Technical Support
- Production credentials: Request from your Kayana account manager
- PSP configuration: Reach out to Kayana Technical Support
- Explore webhook integration for payment notifications
- Learn about refunds and dispute handling
- Set up recurring payments for subscriptions
Next steps: If you prefer Stripe as your payment service provider, refer to our guide on building an advanced Kayana integration for RYFT in PHP.



