Building an Advanced Kayana Integration with RYFT in Node.js
Last updated Mar 13, 2026
This guide shows how to integrate Kayana Payments powered by RYFT using a Node.js backend and a frontend (React / Next.js / HTML). Payment flow: 1️⃣ Backend creates a payment session 2️⃣ Backend retrieves RYFT public key 3️⃣ Frontend loads RYFT SDK 4️⃣ Frontend initialises payment 5️⃣ Customer completes payment
1. Install Dependencies
npm init -y
npm install express cors node-fetch dotenv
2. Project Structure
project
│
├── server.js
├── .env
└── routes
└── payment.js
3. Environment Variables
Create .env
PORT=5000
API_KEY=YOUR_API_KEY
PROPERTY_ID=Prop_039f025cdbc74ca9957dbec1b3e193c1
BASE_URL=https://integration.dev.kayana.co.uk
4. Express Server Setup
server.js
const express = require(“express”);
const cors = require(“cors”);
require(“dotenv”).config();
const paymentRoutes = require(“./routes/payment”);
const app = express();
app.use(cors());
app.use(express.json());
app.use(“/api/payment”, paymentRoutes);
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
5. Create Payment Routes
routes/payment.js
const express = require(“express”);
const fetch = require(“node-fetch”);
const router = express.Router();
const BASE_URL = process.env.BASE_URL;
const API_KEY = process.env.API_KEY;
const PROPERTY_ID = process.env.PROPERTY_ID;
//
// Create Payment Session
//
router.post(“/create-payment”, async (req, res) => {
try {
const response = await fetch(`${BASE_URL}/web/payment`, {
method: “POST”,
headers: {
“Content-Type”: “application/json”,
“x-api-key”: API_KEY,
type: “business”,
},
body: JSON.stringify({
property_id: PROPERTY_ID,
psp_code: “RYFT”,
currency_code: “GBP”,
amount: 100.5,
customer_email: “customer@example.com”,
payment_mode: “CARD”,
}),
});
const data = await response.json();
res.status(response.status).json(data);
} catch (error) {
res.status(500).json({
error: “Payment session creation failed”,
details: error.message,
});
}
});
//
// Get RYFT Public Key
//
router.get(“/public-key”, async (req, res) => {
try {
const response = await fetch(`${BASE_URL}/web/payment/public-key`, {
method: “GET”,
headers: {
“x-api-key”: API_KEY,
“x-psp-code”: “RYFT”,
“x-currency-code”: “GBP”,
type: “business”,
},
});
const data = await response.json();
res.json(data);
} catch (error) {
res.status(500).json({
error: “Failed to fetch public key”,
details: error.message,
});
}
});
module.exports = router;
6. Frontend Integration
Load the RYFT SDK
<script src=”https://embedded.ryftpay.com/v2/ryft.min.js”></script>
7. Initialise Payment on Frontend
async function initPayment() {
// Get public key
const keyRes = await fetch(“/api/payment/public-key”);
const keyData = await keyRes.json();
const publicKey = keyData.data.public_key;
// Create payment session
const paymentRes = await fetch(“/api/payment/create-payment”, {
method: “POST”,
});
const paymentData = await paymentRes.json();
const clientSecret = paymentData.data.client_secret;
// Initialize RYFT
window.Ryft.init({
publicKey,
clientSecret,
});
}
8. Complete Payment
async function payNow() {
const paymentSession = await window.Ryft.attemptPayment();
if (
paymentSession.status === “Approved” ||
paymentSession.status === “Captured”
) {
alert(“Payment Successful”);
} else {
alert(“Payment Failed”);
}
}
9. Test Cards
| Card | Result |
| 4242 4242 4242 4242 | Success |
| 4000 0025 0000 3155 | 3D Secure |
| 4000 0000 0000 9995 | Declined |
Use:
- Any future expiry
- Any CVC
10. Production Checklist
Before going live:
Switch base URL
https://integration.kayana.co.uk
Use production API key Enable RYFT PSP in Kayana Add payment webhooks
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 |
| x-property-id | String | Yes | property id |
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
}
}
Security Best Practices
Never expose:
API keys client_secret payment session logs
Always:
Call the Kayana API from the backend Use environment variables Validate payment status server-side
If you want, I can also give you a much better production-ready version:
- Full React + Node.js RYFT integration
- Proper payment component UI
- Webhook verification
- Error handling
- TypeScript version
Just tell me and I’ll share a complete real-world production implementation.
Next Steps
If you prefer to use RYFT as your Payment Service Provider (PSP), Kayana also supports a full RYFT integration flow. Refer to our guide on how to build an Advanced Kayana Integration with Stripe in Node.js



