Payment links
Connect a collection intent to your invoice and payment notification.
Create a collection intent
Call Create payment link with merchant HMAC. Use external_id for your invoice/order reference and retain the returned short_id, intent ID and checkout_url.
The example includes amount, expiry_days, usage_limit, customer_context and tags. It is not a complete required-field schema. The reviewed local CCAvenue handler specifically checks fixed amount, customer_context and external_id; account PSP mapping is also necessary.
Share the hosted URL
Give the customer the returned checkout_url. Use a link-specific notify_url when you need that intent's callback delivered separately from account defaults.
Track the outcome
Use inbound callbacks for backend payment updates. Public payment status supports the checkout experience but should not replace authenticated transaction recovery and your payment records.
Resolve configuration failures
A documented merchant test reached authentication successfully but returned CCAvenue PSP MID is not assigned to this merchant. This is a merchant/provider mapping issue. Confirm enablement with support instead of repeatedly creating intents.
Request example
Choose Node.js, Python, PHP, Ruby, Java or cURL. Run signed requests on your backend. PHP, Ruby and Java include signing directly. Java requires JDK 17 or newer.
Signing helpers: Node.js · Python. cURL shows the request shape; generate its signature first.
import { signedHeaders } from './signing.mjs';
const path = `/api/v1/collections/intents/api`;
const query = '';
const payload = {
"title": "Invoice INV-2042",
"description": "April subscription renewal",
"usage_limit": 1,
"amount": 2500,
"min_limit": 1,
"max_limit": 50000,
"currency": "INR",
"expiry_days": 7,
"external_id": "INV-2042",
"notify_url": "https://merchant.example.com/hooks/collection-link",
"customer_context": {
"full_name": "Rahul Sharma",
"email_address": "rahul@example.com",
"phone_number": "9876543210",
"dial_code": "+91"
},
"tags": {
"source": "merchant-api",
"channel": "web"
}
};
const body = JSON.stringify(payload);
const headers = signedHeaders({
method: 'POST', path, query, body,
keyId: process.env.ZYTEPE_API_KEY_ID,
secret: process.env.ZYTEPE_API_SECRET,
});
headers['Content-Type'] = 'application/json';
const url = 'https://api.zytepe.com' + path + (query ? '?' + query : '');
const response = await fetch(url, { method: 'POST', headers, body });
if (!response.ok) throw new Error('Request failed: HTTP ' + response.status);
const result = await response.json();
// Store the returned identifiers securely. Do not log customer data.import json, os
from urllib.parse import quote
from urllib.request import Request, urlopen
from zytepe_signing import signed_headers
path = f'/api/v1/collections/intents/api'
query = ''
payload = json.loads('''{
"title": "Invoice INV-2042",
"description": "April subscription renewal",
"usage_limit": 1,
"amount": 2500,
"min_limit": 1,
"max_limit": 50000,
"currency": "INR",
"expiry_days": 7,
"external_id": "INV-2042",
"notify_url": "https://merchant.example.com/hooks/collection-link",
"customer_context": {
"full_name": "Rahul Sharma",
"email_address": "rahul@example.com",
"phone_number": "9876543210",
"dial_code": "+91"
},
"tags": {
"source": "merchant-api",
"channel": "web"
}
}''')
body = json.dumps(payload, separators=(',', ':'), ensure_ascii=False)
headers = signed_headers(
method='POST', path=path, query=query, body=body,
key_id=os.environ['ZYTEPE_API_KEY_ID'],
secret=os.environ['ZYTEPE_API_SECRET'],
)
headers['Content-Type'] = 'application/json'
url = 'https://api.zytepe.com' + path + ('?' + query if query else '')
request = Request(url, method='POST', headers=headers, data=body.encode('utf-8'))
with urlopen(request, timeout=30) as response:
result = json.load(response)
# Store identifiers securely; handle HTTPError/URLError in your application.<?php
// PHP 8+ with the cURL extension. Run on your server.
function envRequired(string $name): string {
$value = getenv($name);
if ($value === false || $value === '') throw new RuntimeException('Set ' . $name);
return $value;
}
$path = '/api/v1/collections/intents/api';
$query = '';
$body = <<<'JSON'
{
"title": "Invoice INV-2042",
"description": "April subscription renewal",
"usage_limit": 1,
"amount": 2500,
"min_limit": 1,
"max_limit": 50000,
"currency": "INR",
"expiry_days": 7,
"external_id": "INV-2042",
"notify_url": "https://merchant.example.com/hooks/collection-link",
"customer_context": {
"full_name": "Rahul Sharma",
"email_address": "rahul@example.com",
"phone_number": "9876543210",
"dial_code": "+91"
},
"tags": {
"source": "merchant-api",
"channel": "web"
}
}
JSON;
$headers = [];
$key = envRequired('ZYTEPE_API_KEY_ID');
$secret = envRequired('ZYTEPE_API_SECRET');
$timestamp = (string) time();
$nonce = bin2hex(random_bytes(16));
$canonical = implode("\n", ['POST', $path, $query, $timestamp, $nonce, hash('sha256', $body)]);
$signature = hash_hmac('sha256', $canonical, $secret);
$headers = ["X-API-KEY-ID: $key", "X-API-TIMESTAMP: $timestamp", "X-API-NONCE: $nonce", "X-API-SIGNATURE: $signature"];
$headers[] = 'Content-Type: application/json';
$url = 'https://api.zytepe.com' . $path . ($query !== '' ? '?' . $query : '');
$ch = curl_init($url);
curl_setopt_array($ch, [CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => $headers,
CURLOPT_TIMEOUT => 30, CURLOPT_POSTFIELDS => $body]);
$response = curl_exec($ch);
if ($response === false) throw new RuntimeException(curl_error($ch));
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($status >= 400) throw new RuntimeException('HTTP ' . $status);
$result = json_decode($response, true, 512, JSON_THROW_ON_ERROR);
// Store returned identifiers securely. Recover uncertain payments before retrying.require 'net/http'
require 'uri'
require 'json'
require 'openssl'
require 'securerandom'
path = '/api/v1/collections/intents/api'
query = ''
body = <<~'JSON'.chomp
{
"title": "Invoice INV-2042",
"description": "April subscription renewal",
"usage_limit": 1,
"amount": 2500,
"min_limit": 1,
"max_limit": 50000,
"currency": "INR",
"expiry_days": 7,
"external_id": "INV-2042",
"notify_url": "https://merchant.example.com/hooks/collection-link",
"customer_context": {
"full_name": "Rahul Sharma",
"email_address": "rahul@example.com",
"phone_number": "9876543210",
"dial_code": "+91"
},
"tags": {
"source": "merchant-api",
"channel": "web"
}
}
JSON
headers = {}
key = ENV.fetch('ZYTEPE_API_KEY_ID')
secret = ENV.fetch('ZYTEPE_API_SECRET')
timestamp = Time.now.to_i.to_s
nonce = SecureRandom.hex(16)
canonical = ['POST', path, query, timestamp, nonce, OpenSSL::Digest::SHA256.hexdigest(body)].join("\n")
signature = OpenSSL::HMAC.hexdigest('SHA256', secret, canonical)
headers = { 'X-API-KEY-ID' => key, 'X-API-TIMESTAMP' => timestamp,
'X-API-NONCE' => nonce, 'X-API-SIGNATURE' => signature }
headers['Content-Type'] = 'application/json'
uri = URI('https://api.zytepe.com' + path + (query.empty? ? '' : '?' + query))
request = Net::HTTP::Post.new(uri, headers)
request.body = body
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true, open_timeout: 10, read_timeout: 30) do |http|
http.request(request)
end
raise 'HTTP ' + response.code unless response.is_a?(Net::HTTPSuccess)
result = JSON.parse(response.body)
# Store returned identifiers securely. Recover uncertain payments before retrying.// Java 17+. Save as ZytePeExample.java and run on your server.
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.*;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.time.Instant;
import java.security.MessageDigest;
import java.util.HexFormat;
import java.util.UUID;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
public class ZytePeExample {
private static String env(String name) {
String value = System.getenv(name);
if (value == null || value.isBlank()) throw new IllegalArgumentException("Set " + name);
return value;
}
public static void main(String[] args) throws Exception {
String path = "/api/v1/collections/intents/api";
String query = "";
String body = """
{
"title": "Invoice INV-2042",
"description": "April subscription renewal",
"usage_limit": 1,
"amount": 2500,
"min_limit": 1,
"max_limit": 50000,
"currency": "INR",
"expiry_days": 7,
"external_id": "INV-2042",
"notify_url": "https://merchant.example.com/hooks/collection-link",
"customer_context": {
"full_name": "Rahul Sharma",
"email_address": "rahul@example.com",
"phone_number": "9876543210",
"dial_code": "+91"
},
"tags": {
"source": "merchant-api",
"channel": "web"
}
}
""".stripTrailing();
var request = HttpRequest.newBuilder(URI.create(
"https://api.zytepe.com" + path + (query.isEmpty() ? "" : "?" + query)))
.timeout(Duration.ofSeconds(30));
String key = env("ZYTEPE_API_KEY_ID");
String secret = env("ZYTEPE_API_SECRET");
String timestamp = Long.toString(Instant.now().getEpochSecond());
String nonce = UUID.randomUUID().toString().replace("-", "");
String bodyHash = HexFormat.of().formatHex(MessageDigest.getInstance("SHA-256")
.digest(body.getBytes(StandardCharsets.UTF_8)));
String canonical = String.join("\n", "POST", path, query, timestamp, nonce, bodyHash);
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
String signature = HexFormat.of().formatHex(mac.doFinal(canonical.getBytes(StandardCharsets.UTF_8)));
request.header("X-API-KEY-ID", key).header("X-API-TIMESTAMP", timestamp)
.header("X-API-NONCE", nonce).header("X-API-SIGNATURE", signature);
request.header("Content-Type", "application/json");
request.method("POST", HttpRequest.BodyPublishers.ofString(body, StandardCharsets.UTF_8));
var client = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(10)).build();
var response = client.send(request.build(), HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));
if (response.statusCode() < 200 || response.statusCode() >= 300)
throw new IllegalStateException("HTTP " + response.statusCode());
String result = response.body(); // Parse JSON using your application's JSON library.
// Store identifiers securely. Recover uncertain payments before retrying.
}
}curl --request POST \
--url 'https://api.zytepe.com/api/v1/collections/intents/api' \
--header 'X-API-KEY-ID: <key-id>' \
--header 'X-API-TIMESTAMP: <unix-seconds>' \
--header 'X-API-NONCE: <fresh-nonce>' \
--header 'X-API-SIGNATURE: <signature>' \
--header 'Content-Type: application/json' \
--data-binary @request.jsonRequest fields
Optional fields can be omitted. Conditional fields depend on the payment method. Labels reflect the reviewed API schemas and handler checks.
titleRequiredCheckout title.
amountRequiredPayment amount in the documented currency.
external_idRequiredYour order or invoice reference; required by the API checkout handler.
customer_contextRequiredCustomer identity; required by the API checkout handler.
customer_context.full_nameRequiredRequired customer detail.
customer_context.email_addressRequiredRequired customer detail.
customer_context.phone_numberRequiredRequired customer detail.
descriptionOptionalAdditional checkout description.
usage_limitOptionalDefaults to 1.
min_limitOptionalDefaults to 1.0000.
max_limitOptionalUpper amount limit; source examples do not establish its effect on a fixed-amount checkout.
currencyOptionalDefaults to INR.
expiry_daysOptionalDefaults to 7 days.
customer_context.dial_codeOptionalDefaults to +91.
notify_urlOptionalLink-specific callback destination.
tagsOptionalAdditional business context.
Request body
{
"title": "Invoice INV-2042",
"description": "April subscription renewal",
"usage_limit": 1,
"amount": 2500,
"min_limit": 1,
"max_limit": 50000,
"currency": "INR",
"expiry_days": 7,
"external_id": "INV-2042",
"notify_url": "https://merchant.example.com/hooks/collection-link",
"customer_context": {
"full_name": "Rahul Sharma",
"email_address": "rahul@example.com",
"phone_number": "9876543210",
"dial_code": "+91"
},
"tags": {
"source": "merchant-api",
"channel": "web"
}
}Example response
{
"success": true,
"message": "Payment link created via API.",
"data": {
"id": "49c2ebf2-f651-473e-ab78-6c154665df92",
"short_id": "s0oJRkuT6OY",
"title": "Invoice INV-2042",
"description": "April subscription renewal",
"fixed_amount": "2500.0000",
"min_limit": "1.0000",
"max_limit": null,
"currency": "INR",
"usage_limit": 1,
"current_usage_count": 0,
"status": "active",
"is_active": true,
"expires_at": "2026-05-10T10:30:00Z",
"checkout_url": "https://pay.zytepe.com/s0oJRkuT6OY"
}
}