Skip to main content

AI & LLM integration guide

Are you building your Divit integration with the help of an AI coding assistant like Gemini, Cursor, GitHub Copilot, ChatGPT, or Cline?

This guide is designed specifically for you to copy and feed directly into your LLM prompt, or save as an instructions file in your workspace (such as .cursorrules or .clinerules). Providing this high-density reference sheet allows your AI coding assistant to generate perfect, ready-to-run integration code on the first try.


1. Prerequisites & Environment Variables

Before asking your LLM to write code, make sure you configure your environment variables (e.g. in your .env or configuration config helper). Feed these variable names to your LLM:

  • DIVIT_ENV: Current environment mode (sandbox or production).
  • DIVIT_API_BASE_URL: Base API URL (https://sandbox-api.divit.dev or https://api.divit.com.hk).
  • DIVIT_API_KEY: Secret merchant credential starting with dvt_.
  • DIVIT_SIGNATURE_KEY: Signature secret configured in the Divit Merchant Admin Portal for webhook validation.

2. Fast Copy-Paste Prompt for LLMs

If you are using a chat-based LLM (like Gemini or ChatGPT) to write your integration, copy and paste the markdown block below as your starting prompt:

I want to integrate Divit Pay Now (FPS bank-to-bank checkout) into my backend.
Here is the official Divit reference sheet. Please write clean, secure code for:
1. Initializing the API client (Phase 1)
2. Creating a payment order (POST /directpay/o/order) (Phase 2)
3. Directing client browsers/webviews (Phase 3)
4. Handling secure asynchronous webhook event callbacks (success = 2001, expired = 4001) with cryptographic verification using X-DIVIT-SIGNATURE and HMAC-SHA256 (Phase 4)
5. Building a fallback endpoint to query order status manually (GET /paynow/orders/{orderID}/status) (Phase 5)

---
### Divit API Cheat-Sheet:

- **Sandbox API Host**: https://sandbox-api.divit.dev
- **Production API Host**: https://api.divit.com.hk

#### API Authentication Header:
api-key: <merchant_api_key>
Content-Type: application/json

#### Create Order Payload Structure:
POST /directpay/o/order
{
"order": {
"totalAmount": {
"amount": 12050, // in smallest subunit (cents), e.g. HKD 120.50 -> 12050
"currency": "HKD"
},
"webhookSuccess": "https://your-site.com/success",
"webhookFailure": "https://your-site.com/cancel",
"webhookEvents": "https://your-api.com/api/webhooks/divit",
"merchantRef": "ORDER-10024A"
},
"customer": {
"firstName": "John",
"lastName": "Doe",
"email": "[email protected]",
"tel": "+85291234567",
"language": "en"
}
}
Response: { "code": 0, "message": "OK", "data": { "redirectURI": "...", "orderID": "..." } }

#### Webhook Signature Verification Algorithm:
- Header: X-DIVIT-SIGNATURE (format: t=timestamp,s1=signature_hash)
- Formula: HMAC_SHA256(secret_key, timestamp + "." + rawBodyString)
- NOTE: You must use the raw, unparsed request body string for HMAC calculation. Re-stringifying parsed JSON will corrupt formatting and break signature verification.
---

Please generate the code in <Your Programming Language / Framework>.

3. Reusable Code Templates

These verified templates can be copy-pasted or referenced directly by your LLM to implement order creation and webhook validation.

const express = require('express');
const crypto = require('crypto');
const app = express();

const DIVIT_API_KEY = process.env.DIVIT_API_KEY;
const DIVIT_SIGNATURE_KEY = process.env.DIVIT_SIGNATURE_KEY;
const DIVIT_API_BASE = process.env.DIVIT_API_BASE_URL || 'https://sandbox-api.divit.dev';

// IMPORTANT: Express must capture the raw body for signature verification
app.post('/api/webhooks/divit', express.raw({ type: 'application/json' }), (req, res) => {
const signatureHeader = req.headers['x-divit-signature'];
const rawBody = req.body.toString('utf-8');

// Verify signature
if (!verifyDivitSignature(signatureHeader, rawBody, DIVIT_SIGNATURE_KEY)) {
return res.status(401).send('Invalid Signature');
}

// Parse body after verification
const payload = JSON.parse(rawBody);
const { eventId } = payload.event;
const { OrderID, MerchantRef } = payload.eventData;

if (eventId === 2001) {
// Payment Succeeded - Fulfill the order
console.log(`Payment successful for order ${MerchantRef} (Divit ID: ${OrderID})`);
} else if (eventId === 4001) {
// Payment Expired
console.log(`Payment expired for order ${MerchantRef}`);
}

res.status(200).json({ received: true });
});

function verifyDivitSignature(headerVal, rawBody, signatureKey) {
if (!headerVal || !rawBody || !signatureKey) return false;

const params = {};
headerVal.split(',').forEach(item => {
const [key, value] = item.trim().split('=');
params[key] = value;
});

const timestamp = params['t'];
const expectedSignature = params['s1'];

if (!timestamp || !expectedSignature) return false;

const signatureContent = `${timestamp}.${rawBody}`;
const calculatedSignature = crypto
.createHmac('sha256', signatureKey)
.update(signatureContent)
.digest('base64');

try {
return crypto.timingSafeEqual(
Buffer.from(calculatedSignature),
Buffer.from(expectedSignature)
);
} catch (e) {
return calculatedSignature === expectedSignature;
}
}

4. Workspace Rule File (e.g., .cursorrules)

If you are using Cursor, VS Code + Cline, or Copilot, you can place a file named .cursorrules or .github/copilot-instructions.md at the root of your project directory.

Copy and paste the template below to ensure your workspace LLM never makes integration errors when coding with Divit:

# Divit Pay Now Integration Rules

You are an expert assistant configured to write integrations for Divit Pay Now, a bank-to-bank FPS checkout service in Hong Kong.

## Environment Variables to Bind:
- `DIVIT_ENV`: "sandbox" or "production"
- `DIVIT_API_BASE_URL`: "https://sandbox-api.divit.dev" or "https://api.divit.com.hk"
- `DIVIT_API_KEY`: Secret merchant key (starts with "dvt_")
- `DIVIT_SIGNATURE_KEY`: Signature webhook verification secret

## Core Rules & Pitfalls to Avoid:

1. **Webhook Raw Request Body (CRITICAL)**:
- When verifying the `X-DIVIT-SIGNATURE` header, ALWAYS use the RAW, unparsed bytes/string of the incoming HTTP request body.
- DO NOT parse the body into a JSON object and then call `JSON.stringify()` or similar, as character spacing/ordering differences will result in a signature mismatch.

2. **Currency Subunit Rule**:
- Divit uses the smallest currency subunit format for transaction values.
- HKD amounts must be multiplied by 100 and parsed as an integer (e.g., HKD $120.50 -> `12050`).

3. **Signature Verification Signature Content**:
- Parse the `X-DIVIT-SIGNATURE` header to extract the timestamp `t` and signature `s1`.
- Reconstruct the signature content string exactly as: `t + "." + rawRequestBody`.
- Generate the Base64 HMAC-SHA256 signature using the local `DIVIT_SIGNATURE_KEY` as secret.
- Use a constant-time comparison helper (like `crypto.timingSafeEqual` in Node or `hmac.compare_digest` in Python) to prevent timing attacks.

4. **Security Best Practice**:
- Never expose the `api-key` or `signature-key` on the client-side. Keep them inside backend environment variables (`process.env` or `.env` files).

5. Sandbox Testing & Verification Guide

To test and simulate the payment journey end-to-end without spending real currency:

  1. Initiate a standard order creation while in sandbox mode (DIVIT_ENV=sandbox / Base URL https://sandbox-api.divit.dev).
  2. Redirect to the generated redirectURI returned in the response object.
  3. Open the FPS Payment Simulator at bank.divit.dev.
  4. Input the reference ID or scan the sandbox payment QR code on the payment screen to initiate a mockup payment.
  5. Confirm that your callback server receives the webhook event 2001 (Payment Success) and processes the transaction correctly.