Merchant Registration (v2)
To register as a merchant with Paylink, the following steps need to be completed:
Partner Onboarding API Documentation
Overview
The Partner Onboarding API provides endpoints for partners to register and authenticate with the Paylink platform. This documentation covers the key endpoints required for partner registration, identity verification through Nafath, and authentication.
Table of Contents
Authentication
Most partner registration endpoints require authentication with the MERCHANT_API_ACCOUNT
role. The authentication flow is:
- First, obtain a JWT token using the
/api/partner/auth
endpoint - Include the JWT token in the
Authorization
header for subsequent requests
Authentication Header Format
Authorization: Bearer <JWT_TOKEN>
API Endpoints
Partner Authentication
Authenticate a partner and receive a JWT token for subsequent API calls.
Endpoint: POST /api/partner/auth
Authentication Required: No (This is a public endpoint)
Request Body
Field | Type | Required | Description | Example |
---|---|---|---|---|
profileNo | String | Yes | Partner profile number | "12312312" |
apiKey | String | Yes | Partner API key | "your-api-key" |
persistToken | Boolean | No | Whether to persist token (30 hours) or not (30 minutes) | true |
Request Example
{
"profileNo": "12312312",
"apiKey": "your-api-key-here",
"persistToken": true
}
Response
Success Response (200 OK)
{
"id_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
Response Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
cURL Example
curl -X POST https://api.paylink.sa/api/partner/auth \
-H "Content-Type: application/json" \
-d '{
"profileNo": "12312312",
"apiKey": "your-api-key",
"persistToken": true
}'
Create Merchant Account
Creates a new merchant with merchant registration details.
Endpoint: POST /api/partner/register/create-account
Authentication Required: Yes
Request Body
Field | Type | Required | Description | Example |
---|---|---|---|---|
sessionUuid | String | Yes | Session UUID (any UUID) | "12345678-1234-1234-1234-123456789012" |
registrationType | String | Yes | Type of registration (cr/freelancer) | "cr" |
licenseNumber | String | Yes | Commercial Registration unified number or Freelancer license number | "7041231231" |
mobileNumber | String | Yes | Mobile number with country code | "0501234567" |
hijriYear | String | Yes | Hijri birth year | "1401" |
hijriMonth | String | Yes | Hijri birth month | "12" |
hijriDay | String | Yes | Hijri birth day | "15" |
partnerProfileNo | String | Yes | Partner profile number | "12312312" |
iban | String | Yes | Saudi IBAN | "SA1234567890123456789012" |
bankSwiftName | String | Yes | Bank SWIFT code | "RJHISARI" |
monthlySalesVolume | Double | Yes | Expected monthly sales volume | 10000.0 |
categoryDescription | String | Yes | Business category | "Grocery" |
sellingScope | String | Yes | Selling scope (domestic/global) | "domestic" |
nationalId | String | Yes | National ID number | "1234567890" |
String | Yes | Email address | "[email protected]" | |
firstName | String | Yes | First name | "Ahmed" |
lastName | String | Yes | Last name | "Ali" |
password | String | Yes | Account password | "SecurePassword123!" |
nafathCallbackUrl | String | Yes | URL for Nafath callbacks (Webhook for Nafath Status Change) | "https://partner.example.com/nafath-callback" |
nafathCallbackHeader1Name | String | No | Custom header name for callback | "X-Partner-Auth" |
nafathCallbackHeader1Value | String | No | Custom header value for callback | "partner-secret" |
nafathCallbackHeader2Name | String | No | Second custom header name | "X-Request-ID" |
nafathCallbackHeader2Value | String | No | Second custom header value | "unique-request-id" |
socialMedia1-7 | String | No | Social media URLs | "https://twitter.com/merchant" |
globalReason | String | No | Reason for global selling (if applicable) | "International customers" |
isDigital | Boolean | No | Whether business is digital | true |
monthlySales | Double | No | Current monthly sales | 5000.0 |
avgTicketSize | Double | No | Average transaction amount | 150.0 |
Request Example
{
"sessionUuid": "12345678-1234-1234-1234-123456789012",
"registrationType": "cr",
"licenseNumber": "2041231231",
"mobileNumber": "0501234567",
"hijriYear": "1443",
"hijriMonth": "12",
"hijriDay": "15",
"partnerProfileNo": "12312312",
"iban": "SA1234567890123456789012",
"bankSwiftName": "RJHISARI",
"monthlySalesVolume": 10000.0,
"categoryDescription": "Grocery",
"sellingScope": "domestic",
"nationalId": "1234567890",
"email": "[email protected]",
"firstName": "Ahmed",
"lastName": "Ali",
"password": "SecurePassword123!",
"nafathCallbackUrl": "https://partner.example.com/nafath-callback",
"isDigital": true
}
Response
Success Response (200 OK)
{
"mobile": "0501234567",
"sessionUuid": "12345678-1234-1234-1234-123456789012",
"signature": "9874ffff8710904aecb139f0f9f7f3e35bdad3ae99d26a93c94fb74c208e828a",
"success": true,
"note": "Account created successfully, proceed with Nafath verification",
"nafathRandomNo": "42"
}
cURL Example
curl -X POST https://api.paylink.sa/api/partner/register/create-account \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-d '{
"sessionUuid": "12345678-1234-1234-1234-123456789012",
"registrationType": "cr",
"licenseNumber": "2041231231",
"mobileNumber": "0501234567",
"hijriYear": "1443",
"hijriMonth": "12",
"hijriDay": "15",
"partnerProfileNo": "12312312",
"iban": "SA1234567890123456789012",
"bankSwiftName": "RJHISARI",
"monthlySalesVolume": 10000.0,
"categoryDescription": "Grocery",
"sellingScope": "domestic",
"nationalId": "1234567890",
"email": "[email protected]",
"firstName": "Ahmed",
"lastName": "Ali",
"password": "SecurePassword123!",
"nafathCallbackUrl": "https://partner.example.com/nafath-callback"
}'
Check Nafath Status
Check the status of Nafath identity verification for a registration session.
Endpoint: POST /api/partner/register/check-nafath
Authentication Required: Yes (MERCHANT_API_ACCOUNT role)
Request Body
Field | Type | Required | Description | Example |
---|---|---|---|---|
sessionUuid | String | Yes | Session UUID from registration | "12345678-1234-1234-1234-123456789012" |
mobileNumber | String | Yes | Mobile number of the user | "966501234567" |
partnerProfileNo | String | Yes | Partner profile number | "12312312" |
Request Example
{
"sessionUuid": "12345678-1234-1234-1234-123456789012",
"mobileNumber": "0501234567",
"partnerProfileNo": "12312312"
}
Response
Success Response (200 OK)
{
"signature": "1234567890abcdef",
"mobile": "0501234567",
"sessionUuid": "12345678-1234-1234-1234-123456789012",
"nafathNumber": "42",
"status": "ACCEPTED",
"email": "[email protected]",
"errorMessage": null,
"activatingAccountResponse": {
"merchantId": 12345,
"apiKey": "newly-generated-api-key",
"success": true
}
}
Nafath Status Values
PENDING
- Nafath verification is pendingACCEPTED
- Nafath verification successfulREJECTED
- Nafath verification failedEXPIRED
- Nafath session expired
cURL Example
curl -X POST https://api.paylink.sa/api/partner/register/check-nafath \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-d '{
"sessionUuid": "12345678-1234-1234-1234-123456789012",
"mobileNumber": "0501234567",
"partnerProfileNo": "12312312"
}'
Integration Flow
The typical partner onboarding flow follows these steps:
sequenceDiagram
participant Partner
participant PayLink API
participant Nafath
Partner->>PayLink API: 1. POST /api/partner/auth
PayLink API-->>Partner: JWT Token
Partner->>PayLink API: 2. POST /api/partner/register/create-account
PayLink API-->>Partner: Session UUID & Nafath Number
Partner->>Nafath: 3. User completes Nafath verification
Nafath-->>PayLink API: Verification result
Partner->>PayLink API: 4. POST /api/partner/register/check-nafath
PayLink API-->>Partner: Verification status & API credentials
Detailed Integration Steps
-
Authentication
- Call
/api/partner/auth
with partner credentials - Store the JWT token for subsequent requests
- Call
-
Create Account
- Collect all required merchant information
- Call
/api/partner/register/create-account
with complete details - Save the
sessionUuid
andnafathRandomNo
from response
-
Nafath Verification
- Display the
nafathRandomNo
to the user - Direct user to complete Nafath verification in Absher app
- The system will receive callbacks to the provided
nafathCallbackUrl
- Display the
-
Check Status
- Poll
/api/partner/register/check-nafath
to check verification status - Recommended polling interval: 5-10 seconds
- Continue until status is
ACCEPTED
,REJECTED
, orEXPIRED
- Poll
-
Handle Results
- If
ACCEPTED
: Save the merchant credentials fromactivatingAccountResponse
- If
REJECTED
: Display error and allow retry - If
EXPIRED
: Start new registration session
- If
Error Handling
All endpoints return standard HTTP status codes and error responses:
Common Error Responses
401 Unauthorized
{
"type": "https://www.jhipster.tech/problem/problem-with-message",
"title": "Unauthorized",
"status": 401,
"detail": "Full authentication is required to access this resource"
}
400 Bad Request
{
"type": "https://www.jhipster.tech/problem/constraint-violation",
"title": "Bad Request",
"status": 400,
"violations": [
{
"field": "mobileNumber",
"message": "must not be null"
}
]
}
403 Forbidden
{
"type": "https://www.jhipster.tech/problem/problem-with-message",
"title": "Forbidden",
"status": 403,
"detail": "Access is denied"
}
500 Internal Server Error
{
"type": "https://www.jhipster.tech/problem/problem-with-message",
"title": "Internal Server Error",
"status": 500,
"detail": "An unexpected error occurred"
}
Error Handling Best Practices
-
Implement Retry Logic
- For 5xx errors: Implement exponential backoff
- For 401 errors: Refresh authentication token
- For 400 errors: Fix request data before retry
-
Session Management
- Store session UUID securely
- Handle session expiration gracefully
- Allow users to restart registration if needed
-
User Communication
- Display clear error messages
- Provide actionable next steps
- Log detailed errors for debugging
Examples
Complete Registration Flow Example
// 1. Authenticate
const authResponse = await fetch('https://rest.paylink.sa/api/partner/auth', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
profileNo: '12312312',
apiKey: 'your-api-key',
persistToken: true
})
});
const { id_token } = await authResponse.json();
// 2. Create Account
const createResponse = await fetch('https://rest.paylink.sa/api/partner/register/create-account', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${id_token}`
},
body: JSON.stringify({
sessionUuid: generateUUID(),
registrationType: 'cr',
licenseNumber: '2041231231',
mobileNumber: '0501234567',
// ... other required fields
})
});
const { sessionUuid, nafathRandomNo } = await createResponse.json();
// 3. Display Nafath number to user
console.log(`Please verify in Absher app using number: ${nafathRandomNo}`);
// 4. Poll for verification status
const checkStatus = async () => {
const statusResponse = await fetch('https://rest.paylink.sa/api/partner/register/check-nafath', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${id_token}`
},
body: JSON.stringify({
sessionUuid,
mobileNumber: '0501234567',
partnerProfileNo: '12312312'
})
});
const result = await statusResponse.json();
if (result.status === 'ACCEPTED') {
console.log('Registration successful!', result.activatingAccountResponse);
} else if (result.status === 'PENDING') {
// Continue polling
setTimeout(checkStatus, 5000);
} else {
console.error('Registration failed:', result.errorMessage);
}
};
// Start polling
checkStatus();
Updated about 5 hours ago