Switch Wallet Merchant API Documentation

Becoming a Merchant

Welcome to the Switch Wallet Merchant API. Integrating with our powerful RESTful API and webhook service is the first step to leveraging our platform. The process begins with completing your business verification and setting up your account.

Onboarding Steps

  1. Complete KYB: Finalize the "Know Your Business" (KYB) process with our team.
  2. Provide Details: Submit your business account and SUPER admin profile information as structured below.

Merchant Data Structure

You will need to provide the following data. The table below illustrates the required fields and their validation rules.

Merchant Company

Field Type Required Description
id string (UUID) Yes Unique identifier for the merchant company
name string Yes Company name
tagline string Yes Company tagline or slogan
companyEmail string (email) Yes Company email address (lowercase)
countryCode string (2 chars) Yes ISO country code (2 characters)
logoUrl string (URI) No URL to company logo
websiteUrl string (URI) No Company website URL

Merchant Account

Field Type Required Description
email string (email) Yes Account email address (lowercase)
firstName string Yes First name of the account holder
lastName string Yes Last name of the account holder
phoneNumber string Yes Phone number
countryCode string (2 chars) Yes ISO country code (2 characters)

General API Response

All API responses follow a standardized JSON structure. This ensures consistency and predictability when you interact with our endpoints.

Response Object Structure

{
  "data": {},
  "status": "success",
  "statusCode": 200,
  "message": "Action Completed",
  "action": null,
  "messageLanguageCode": "app_001",
  "details": null,
  "cacheTTL": 20
}

Field Descriptions

Field Description
data The main content returned by the API. Can be an object, array, or null.
status Indicates the outcome of the request. Possible values are 'success' or 'failed'.
statusCode The standard HTTP response status code.
message A human-readable message to be displayed to the end-user (e.g., "Registration completed").
messageLanguageCode A unique code for the message, which can be used by frontend clients for translations.
action A specific action the frontend should take, e.g., 'load_2fa_screen'. See action documentation for details.
details Contains detailed debug messages or error logs for the developer. This field is not returned in production environments.
cacheTTL Time-To-Live in seconds. Indicates how long the response is cached on the server. You may use this to manage your own caching logic.

Account Setup & Configuration

[Registration & Activation] After KYB, we create your account and send an activation link to your `merchantAccount.email`. Follow it to access the Switch Wallet Dashboard, complete registration, and set up 2FA.

[Webhook URL] In the dashboard, set your webhook URL. We send notifications here and often require acknowledgements.

[RSA Public Key] For direct server-to-server API calls, you must generate an RSA key pair. In the Switch Wallet Dashboard, upload your public key. This allows our servers to verify requests signed with your corresponding private key.

API Authentication

Our API uses RSA signature authentication for all direct server-to-server communication. This is a stateless method ideal for automated operations that uses a public/private key pair and does not require a login session.

RSA Signature (For Direct Merchant API)

This is a stateless method ideal for automated, server-to-server communication. It uses a public/private key pair and does not require a login session. All direct Merchant API calls (e.g., transfers, queries) must use RSA signatures.

RSA Signature (Stateless Merchant API)

For direct server-to-server API calls, you must use RSA signing. This method is stateless and does not require a login session. It proves the request is from you and that the payload has not been tampered with.

Note: Your Merchant Company App Id can be found in your merchant dashboard under the API Keys section. This is the identifier you'll use in the Authorization header for all API requests.

RSA Authentication Flow

  1. Get Your Merchant Company App Id and API Key: Navigate to your merchant dashboard and go to the API Keys section. Copy your Merchant Company App Id and your API Key (x-api-key).
  2. Generate Key Pair: You must generate a standard RSA key pair. Keep your private key secret. See the section below for code samples.
  3. Upload Public Key: Upload your public key to the Switch Wallet merchant dashboard.
  4. Construct String-to-Sign: Create a canonical string by sorting the keys in your JSON request payload and joining them (e.g., `amount=5000&destinationAccount=...`).
  5. Sign with Private Key: Use your private key to sign the string from the previous step using the `RSA-SHA256` algorithm.
  6. Send Headers: Make your API request with the following headers:
    • Authorization: `Bearer ` (Get this from your merchant dashboard API Keys section)
    • x-api-key: Your global API key (Get this from your merchant dashboard API Keys section)
    • x-rsa-signature: The Base64-encoded signature you just generated.
    • x-request-timestamp: Current timestamp in milliseconds.
    • x-nonce-string: A random 32-character string.

Generating Your RSA Key Pair

To get started with RSA authentication, you first need to generate a 2048-bit RSA key pair. Run one of the scripts below to create private_key.pem, public_key.pem, and bootstrap.env (containing base64-encoded keys). Keep the private key secure and upload the contents of the public key to your merchant dashboard.

// Save as generateKeys.js and run `node generateKeys.js`
const { generateKeyPair } = require('crypto');
const fs = require('fs');

generateKeyPair('rsa', {
  modulusLength: 2048,
  publicKeyEncoding: { type: 'spki', format: 'pem' },
  privateKeyEncoding: { type: 'pkcs8', format: 'pem' }
}, (err, publicKey, privateKey) => {
  if (err) { return console.error(err); }
  
  // Write PEM files
  fs.writeFileSync('public_key.pem', publicKey);
  fs.writeFileSync('private_key.pem', privateKey);
  
  // Convert to base64 and create bootstrap.env
  const privateB64 = Buffer.from(privateKey).toString('base64').replace(/\n/g, '');
  const publicB64 = Buffer.from(publicKey).toString('base64').replace(/\n/g, '');
  
  const bootstrapEnv = `# Base64-encoded key material (generated ${new Date().toISOString()})
RSA_MERCHANT_PRIVATE_KEY_B64=\"${privateB64}\"
RSA_MERCHANT_PUBLIC_KEY_B64=\"${publicB64}\"
`;
  
  fs.writeFileSync('bootstrap.env', bootstrapEnv);
  
  console.log('✓ Public and private key files generated!');
  console.log('✓ bootstrap.env created with base64-encoded keys');
});

RSA Signature Generation

⚠️ Important: Nested Object Handling

When your payload contains nested objects (like proxyKYCDetails), you must convert them to JSON strings during signature generation. The server expects the exact same string format for verification. See the examples below for proper implementation.

💡 Pro Tip: Include All Fields

Always include all required fields in your payload (even if they're false or null). The server may add default values during validation, which will cause signature verification to fail if not included in your original payload.

💡 Using Base64 Keys from .env

You can load your private key from base64 format stored in your .env file (from bootstrap.env). This is more secure and convenient for production environments.

// Node.js Example for RSA Signature
const crypto = require('crypto');
const fs = require('fs');
require('dotenv').config(); // Load .env file

// Load private key from base64 in .env file
const privateKeyB64 = process.env.RSA_MERCHANT_PRIVATE_KEY_B64;
const privateKey = Buffer.from(privateKeyB64, 'base64').toString('utf-8');

// Alternative: Load from PEM file (if not using .env)
// const privateKey = fs.readFileSync('path/to/your_private_key.pem', 'utf-8');

function generateRsaSignature(privateKey, payload) {
  // Create the exact same sorted string from the payload.
  // Filter out null/undefined values and handle nested objects.
  const stringToSign = Object.keys(payload)
    .filter(key => payload[key] != null && payload[key] !== '')
    .sort()
    .map(key => {
      const value = payload[key];
      // Handle nested objects by converting them to JSON strings
      if (typeof value === 'object' && value !== null) {
        return `${key}=${JSON.stringify(value)}`;
      }
      return `${key}=${value}`;
    })
    .join('&');

  // Sign the string with your private key.
  const signature = crypto
    .createSign('RSA-SHA256')
    .update(stringToSign)
    .sign(privateKey, 'base64');

  return signature;
}

// Example Usage:
const requestPayload = {
    firstName: 'John',
    lastName: 'Doe',
    email: '[email protected]',
    countryCode: 'NG',
    phoneNumber: '08012345678',
    proxyKYCDetails: {
        idCardType: 'PASSPORT',
        idCardNumber: 'A12345678',
        countryOfIssue: 'NG'
    }
};

// Generate nonce for header
const nonceStr = crypto.randomBytes(16).toString('hex');

const rsaSignature = generateRsaSignature(privateKey, requestPayload);
// Send this rsaSignature in the 'x-rsa-signature' header.

Verifying Webhooks

To ensure the security and integrity of data sent to your webhook URL, all outgoing requests from Switch Wallet are signed. We include the X-Webhook-Secret and X-Webhook-Signature headers on every webhook request. You must verify this signature using the Switch Wallet Public Key, which is available for you to copy from your merchant dashboard.

This process guarantees that the webhook was sent by Switch Wallet and that its content has not been altered. The secret header allows you to quickly reject any request that does not match the value configured in your dashboard.

Webhook Verification Flow

  1. Get Public Key: Copy the Switch Wallet Public Key from your merchant dashboard.
  2. Receive Webhook: Your server receives a POST request at your configured webhook URL with JSON body and headers X-Webhook-Secret, X-Webhook-Signature, X-Webhook-Event-Type, and X-Webhook-Timestamp.
  3. Validate Secret: Compare the X-Webhook-Secret header with the webhook secret configured in your dashboard. If it does not match, immediately reject the request.
  4. Extract Signature & Body: Read the raw request body bytes (before JSON parsing) and get the base64-encoded signature from the X-Webhook-Signature header.
  5. Verify Signature: Use the Switch Wallet Public Key to verify that the signature matches the raw request body using RSA-SHA256. If it's valid, process the webhook. Otherwise, discard the request.

Important: Use Raw Body for Verification

⚠️ Critical for Webhook Verification:

When validating the RSA signature, you must verify against the exact raw request body bytes that Switch Wallet sent, not a re-serialized JSON string. Most frameworks provide a way to access the raw body (e.g. request.rawBody, request.get_data(), or php://input).

Switch Wallet signs the raw JSON payload with RSA-SHA256 using our private key; you should verify that signature with the public key exposed in your dashboard.

Webhook Verification Examples

// Node.js Example for Webhook Verification
const crypto = require('crypto');

function verifyWebhookSignature(publicKey, payload) {
  const { signature, ...dataToVerify } = payload;
  if (!signature) return false;

  const stringToVerify = Object.keys(dataToVerify)
    .filter(key => dataToVerify[key] != null && dataToVerify[key] !== '')
    .sort()
    .map(key => {
      const value = dataToVerify[key];
      if (typeof value === 'object' && value !== null) {
        return `${key}=${JSON.stringify(value)}`;
      }
      return `${key}=${value}`;
    })
    .join('&');

  const isVerified = crypto
    .createVerify('RSA-SHA256')
    .update(stringToVerify)
    .verify(publicKey, signature, 'base64');

  return isVerified;
}

// Example Usage in an Express.js route
app.post('/webhook', (req, res) => {
  const switchPublicKey = '-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----'; // From your dashboard
  const isValid = verifyWebhookSignature(switchPublicKey, req.body);

  if (isValid) {
    console.log('Webhook verified successfully.');
    res.status(200).send('OK');
  } else {
    console.error('Webhook verification failed.');
    res.status(400).send('Invalid signature');
  }
});

Webhook Events

Switch Wallet sends webhooks to your configured webhook URL for various events. All webhooks are signed with RSA-SHA256 and include authentication headers. Below are all the webhook events you can expect to receive.

Wallet.ManualRemittanceSweepCreated

Sent when a manual remittance sweep request is created. This occurs when an admin or merchant creates a sweep list item with a transaction hash.

When it's sent:

  • When a sweep list item is created with a valid transaction hash
  • Only if notifyMerchant is set to true
  • After successful validation of the transaction hash

Wallet.RemittanceSweepStatusUpdated

Sent when a remittance sweep status is updated (e.g., when a sweep is completed or fails). This webhook is sent automatically by AsoRock when processing sweeps.

When it's sent:

  • When a sweep is completed successfully (status: COMPLETED)
  • When a sweep fails (status: FAILED)
  • Includes detailed error information if the sweep failed

HTTPS Request Samples

Below are sample code examples showing how to make authenticated HTTPS requests to the Merchant API. These examples demonstrate the proper headers and authentication flow required for all merchant endpoints using RSA signatures.

Required Headers

All merchant API requests require the following headers for authentication and security:

Header Type Description Example
Accept string Content type for the response application/json
x-api-key string API key for general authorization your_api_key_here
Authorization string Bearer token containing your Merchant Company App Id (found in merchant dashboard API Keys section) Bearer YOUR_MERCHANT_COMPANY_APP_ID
x-rsa-signature string Base64-encoded RSA signature for request integrity generated_rsa_signature

Request Sample Code

Here's a complete example of how to make authenticated requests to the Merchant API using RSA signatures:

// Node.js Example - Merchant API Request with RSA Signature
const crypto = require('crypto');
const fs = require('fs');
const axios = require('axios');

const generateRsaSignature = (privateKey, payload) => {
  // Create the exact same sorted string from the payload
  const stringToSign = Object.keys(payload)
    .filter(key => payload[key] != null && payload[key] !== '')
    .sort()
    .map(key => `${key}=${payload[key]}`)
    .join('&');

  // Sign the string with your private key
  const signature = crypto
    .createSign('RSA-SHA256')
    .update(stringToSign)
    .sign(privateKey, 'base64');

  return signature;
};

const makeMerchantRequest = async (
  baseUrl,
  method,
  url,
  payload,
  merchantCompanyAppId,
  apiKey,
  privateKey
) => {
  try {
    const rsaSignature = generateRsaSignature(privateKey, payload);
    
    const headers = {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
      'x-api-key': apiKey,
      'Authorization': `Bearer ${merchantCompanyAppId}`,
      'x-rsa-signature': rsaSignature,
    };

    const config = {
      method: method.toLowerCase(),
      url: `${baseUrl}${url}`,
      headers: headers,
      data: method.toLowerCase() !== 'get' ? payload : undefined
    };

    const response = await axios(config);
    return response.data;
  } catch (error) {
    console.error(`Error during request to ${url}:`, error.response?.data || error.message);
    throw error;
  }
};

// Example usage
const exampleRequest = async () => {
  const privateKey = fs.readFileSync('path/to/your_private_key.pem', 'utf-8');
  const merchantId = 'your_merchant_id';
  const apiKey = 'your_api_key';
  const baseUrl = 'https://api-solid-staging.switchwallet.io';
  
  // Example: Get merchant profile
  const payload = {
    requestTime: Date.now()
  };
  
  // Generate nonce for header
  const nonceStr = crypto.randomBytes(16).toString('hex');
  
  const response = await makeMerchantRequest(
    baseUrl,
    'GET',
    '/api/Merchant/profile',
    payload,
    merchantId,
    apiKey,
    privateKey,
    'NG'
  );
  
  console.log('Response:', response);
};

Header Explanations

Authentication Headers

  • x-api-key: General API authorization key required for all requests
  • Authorization: Bearer token containing your Merchant Company App Id (found in merchant dashboard API Keys section)
  • x-rsa-signature: Base64-encoded RSA signature for request integrity verification
  • x-request-timestamp: Current timestamp in milliseconds for request freshness
  • x-nonce-string: Unique random string for each request to prevent replay attacks

Security & Context Headers

  • Accept: Specifies the expected response format (application/json)
  • Content-Type: Specifies the request body format (application/json)

Pagination

Endpoints that return a list of items are paginated. You can control the pagination using the following parameters in your request payload.

Parameter Type Description
pageId number The page number you want to retrieve. Starts at 1.
perPage number The number of items to return per page.
sort string The sort order. Accepts ASC for ascending or DESC for descending.

API Documentation

For a complete and interactive API reference, including all endpoints, request/response models, and the ability to try out API calls directly, please visit our API documentation.

for swagger lovers (link to the swagger docs)


Copied to clipboard!