Skip to content
Kayana

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

CardResult
4242 4242 4242 4242Success
4000 0025 0000 31553D Secure
4000 0000 0000 9995Declined

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:

HeaderTypeRequiredDescription
x-api-keyStringYesYour API key
typeStringYesMust be set to business
Content-TypeStringYesMust be application/json
x-property-idStringYesproperty id

1. Capture Authorised Payment

Endpoint

POST /web/payment/capture-authorised-payment

Request Parameters

FieldTypeRequiredDescription
order_idStringYesOrder ID
amountNumberYesAmount 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

FieldTypeRequiredDescription
order_idStringYesOrder 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

FieldTypeRequiredDescription
order_idStringYesOrder ID
amountNumberYesRefund 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

FieldTypeRequiredDescription
order_idStringYesOrder 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

Still need a hand?Contact support← All help