API Reference — v1

Build with deVIP

Accept payments, authenticate users, and access wallet data — all through a single, well-documented API designed for Nigerian businesses.

Base URLhttps://api.devip.me/api/v1

OAuth 2.0

Sign in with deVIP — let users auth with their existing account

Wallet Debit

Instantly charge a user's deVIP wallet at checkout

Bank Transfer

Generate unique virtual accounts for bank transfer payments

Webhooks

Receive real-time events when payments succeed

Overview

The deVIP API lets third-party applications integrate deVIP payments and authentication. It supports the OAuth 2.0 Authorization Code flow for user login, direct wallet debit at checkout, and virtual bank account generation for bank transfers.

All endpoints

GET
/oauth/userinfo

Fetch authenticated user profile

POST
/oauth/token

Exchange auth code for access token

GET
/wallet/balance

Get user's wallet balance

POST
/wallet/debit

Charge user's wallet

POST
/transfers/generate

Generate virtual bank account

GET
/transfers/verify/{reference}

Check transfer payment status

POST
/transfers/simulate-pay/{reference}

Simulate bank transfer (sandbox only)

Getting access: The deVIP API is available to verified business partners. Email dev@devip.me to request your client_id, client_secret, and webhook_secret.

Authentication

Server-to-server API requests authenticate using your Client ID and Client Secret as a Bearer token. The OAuth user token is used separately to call /oauth/userinfo.
http
X-Devip-Client-Id: YOUR_CLIENT_ID
Authorization: Bearer YOUR_CLIENT_SECRET
Accept: application/json
Content-Type: application/json
Keep it server-side. Never expose your client_secret in browser JavaScript, mobile apps, or public repositories. All API calls must originate from your backend server.

OAuth 2.0 — Sign in with deVIP

Allow your users to authenticate using their deVIP account via the standard OAuth 2.0 Authorization Code flow. This eliminates separate registration for apps that integrate with deVIP.

Protocol Flow

1Your app redirects the user to the deVIP authorization page
2User logs in to deVIP and approves your requested scopes
3deVIP redirects back to your callback URL with a one-time code
4Your backend exchanges the code for an access_token
5Use the access_token to fetch user profile and wallet data

Step 1 — Redirect the User

Redirect the user's browser to the deVIP authorization endpoint.
url
GET https://devip.me/oauth/authorize
  ?client_id=YOUR_CLIENT_ID
  &redirect_uri=https://yourapp.com/auth/callback
  &scope=profile email phone
  &state=RANDOM_CSRF_TOKEN
Parameter
Type
Req.
Description
client_id
string
Yes
Your registered deVIP client ID
redirect_uri
string
Yes
Callback URL. Must match your registered redirect URIs exactly.
scope
string
No
Space-separated scopes: profile, email, phone, wallet. Defaults to profile email.
state
string
No
A random string to prevent CSRF attacks. Verify it matches on callback.

Step 2 — Handle the Callback

After the user approves, they're redirected to your callback URL with a temporary code. It expires in 10 minutes and is single-use.
url
GET https://yourapp.com/auth/callback?code=TEMP_AUTH_CODE&state=RANDOM_CSRF_TOKEN

Step 3 — Exchange Code for Token

On your server, POST to exchange the code for an access token.
http
POST https://api.devip.me/api/v1/oauth/token
Content-Type: application/json

{
  "grant_type": "authorization_code",
  "code": "TEMP_AUTH_CODE",
  "client_id": "YOUR_CLIENT_ID",
  "client_secret": "YOUR_CLIENT_SECRET",
  "redirect_uri": "https://yourapp.com/auth/callback"
}
json — 200 OK
{
  "access_token": "devip_at_xyz789...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "profile email phone"
}

Step 4 — Fetch User Profile

http
GET https://api.devip.me/api/v1/oauth/userinfo
Authorization: Bearer devip_at_xyz789...
json — 200 OK
{
  "sub": "DEVIP-12345",
  "name": "John Doe",
  "email": "john@example.com",
  "phone": "08012345678",
  "avatar": "https://api.devip.me/avatars/john.png",
  "wallet": {
    "balance": 25000.00,
    "currency": "NGN"
  }
}

Available Scopes

profileUser's name and avatar
emailUser's email address
phoneUser's phone number
walletNGN wallet balance — requires partner approval

Code Examples

javascript (Node.js)
// Express callback handler
app.get('/auth/callback', async (req, res) => {
  const { code, state } = req.query;

  // Verify state to prevent CSRF
  if (state !== req.session.oauthState) {
    return res.status(403).send('Invalid state');
  }

  const response = await fetch('https://api.devip.me/api/v1/oauth/token', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      grant_type: 'authorization_code',
      code,
      client_id: process.env.DEVIP_CLIENT_ID,
      client_secret: process.env.DEVIP_CLIENT_SECRET,
      redirect_uri: 'https://yourapp.com/auth/callback',
    }),
  });

  const { access_token } = await response.json();

  // Fetch user info
  const profileRes = await fetch('https://api.devip.me/api/v1/oauth/userinfo', {
    headers: { Authorization: `Bearer ${access_token}` },
  });
  const profile = await profileRes.json();

  // Log the user in to your app
  req.session.user = profile;
  res.redirect('/dashboard');
});
python (Django / Flask)
import requests

def oauth_callback(request):
    code = request.GET.get('code')
    state = request.GET.get('state')

    # Verify state
    if state != request.session.get('oauth_state'):
        return HttpResponse('Invalid state', status=403)

    # Exchange code for token
    token_res = requests.post('https://api.devip.me/api/v1/oauth/token', json={
        'grant_type': 'authorization_code',
        'code': code,
        'client_id': settings.DEVIP_CLIENT_ID,
        'client_secret': settings.DEVIP_CLIENT_SECRET,
        'redirect_uri': 'https://yourapp.com/auth/callback',
    })
    access_token = token_res.json()['access_token']

    # Get user profile
    profile_res = requests.get('https://api.devip.me/api/v1/oauth/userinfo',
        headers={'Authorization': f'Bearer {access_token}'})
    profile = profile_res.json()

    # Create or update user in your DB
    user, _ = User.objects.get_or_create(email=profile['email'])
    login(request, user)
    return redirect('/dashboard')

Get Wallet Balance

Check a user's NGN wallet balance before initiating checkout. Requires the wallet scope.
http
GET https://api.devip.me/api/v1/wallet/balance?account_id=DEVIP-12345
X-Devip-Client-Id: YOUR_CLIENT_ID
Authorization: Bearer YOUR_CLIENT_SECRET
Parameter
Type
Req.
Description
account_id
string
Yes
The user's deVIP account ID (from the sub field in userinfo)
json — 200 OK
{
  "success": true,
  "data": {
    "balance": 25000.00,
    "currency": "NGN"
  }
}

Debit Wallet

Instantly charge a user's deVIP wallet during checkout. The user must have previously authorized the wallet scope.
Use a unique reference per transaction. Duplicate references are rejected to prevent double charges.
http
POST https://api.devip.me/api/v1/wallet/debit
X-Devip-Client-Id: YOUR_CLIENT_ID
Authorization: Bearer YOUR_CLIENT_SECRET
Content-Type: application/json

{
  "account_id": "DEVIP-12345",
  "amount": 18000.00,
  "reference": "ORDER-REF-abc123"
}
Parameter
Type
Req.
Description
account_id
string
Yes
The user's deVIP account ID
amount
number
Yes
Amount to charge in NGN (e.g. 18000.00)
reference
string
Yes
Your unique order reference. Used for idempotency.
json — 200 OK
{
  "success": true,
  "message": "Wallet debited successfully.",
  "data": {
    "reference": "ORDER-REF-abc123",
    "amount": 18000.00,
    "previous_balance": 25000.00,
    "new_balance": 7000.00
  }
}
javascript (Node.js)
const response = await fetch('https://api.devip.me/api/v1/wallet/debit', {
  method: 'POST',
  headers: {
    'X-Devip-Client-Id': process.env.DEVIP_CLIENT_ID,
    'Authorization': `Bearer ${process.env.DEVIP_CLIENT_SECRET}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    account_id: 'DEVIP-12345',
    amount: 18000.00,
    reference: `ORDER-${Date.now()}`,
  }),
});

const result = await response.json();

if (result.success) {
  // Fulfill the order
  await fulfillOrder(result.data.reference);
}

Bank Transfer Payments

For users who prefer bank transfers, generate a unique virtual bank account number per transaction. The account accepts exactly the invoiced amount and expires in 1 hour.

Generate Virtual Account

http
POST https://api.devip.me/api/v1/transfers/generate
X-Devip-Client-Id: YOUR_CLIENT_ID
Authorization: Bearer YOUR_CLIENT_SECRET
Content-Type: application/json

{
  "client_id": "YOUR_CLIENT_ID",
  "amount": 18000.00,
  "reference": "ORDER-REF-abc123"
}
json — 200 OK
{
  "success": true,
  "data": {
    "bank_name": "Wema Bank",
    "account_number": "9912847294",
    "account_name": "YourApp Payments",
    "amount": 18000.00,
    "reference": "ORDER-REF-abc123",
    "expires_at": "2026-06-15T09:00:00Z"
  }
}

Verify Transfer Status

Poll this endpoint or listen for a webhook to confirm the transfer was received.
http
GET https://api.devip.me/api/v1/transfers/verify/ORDER-REF-abc123
X-Devip-Client-Id: YOUR_CLIENT_ID
Authorization: Bearer YOUR_CLIENT_SECRET
json — 200 OK
{
  "success": true,
  "data": {
    "status": "success",
    "reference": "ORDER-REF-abc123"
  }
}

Webhooks

deVIP sends a signed POST request to your webhook URL whenever a payment event occurs. Always verify the signature before fulfilling orders.

Webhook Payload

json
POST https://yourapp.com/webhooks/devip

{
  "event": "charge.success",
  "data": {
    "reference": "ORDER-REF-abc123",
    "status": "success",
    "amount": 18000.00
  }
}

Verifying Signatures

Compute an HMAC-SHA256 hash of the raw request body using your webhook_secret. Compare it against the x-devip-signature header.
javascript (Node.js / Express)
const crypto = require('crypto');

app.post('/webhooks/devip', express.raw({ type: 'application/json' }), (req, res) => {
  const signature = req.headers['x-devip-signature'];
  const rawBody = req.body; // must be raw Buffer

  const expected = crypto
    .createHmac('sha256', process.env.DEVIP_WEBHOOK_SECRET)
    .update(rawBody)
    .digest('hex');

  if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature))) {
    return res.status(401).json({ message: 'Invalid signature' });
  }

  const payload = JSON.parse(rawBody);

  if (payload.event === 'charge.success') {
    await fulfillOrder(payload.data.reference, payload.data.amount);
  }

  res.status(200).json({ received: true });
});
php (Laravel)
public function handleWebhook(Request $request)
{
    $rawBody = $request->getContent();
    $signature = $request->header('x-devip-signature');
    $secret = config('devip.webhook_secret');

    $expected = hash_hmac('sha256', $rawBody, $secret);

    if (!hash_equals($expected, $signature)) {
        return response()->json(['message' => 'Invalid signature'], 401);
    }

    $payload = json_decode($rawBody, true);

    if ($payload['event'] === 'charge.success') {
        $this->fulfillOrder(
            $payload['data']['reference'],
            $payload['data']['amount']
        );
    }

    return response()->json(['received' => true]);
}
python (Django)
import hmac, hashlib, json
from django.http import JsonResponse

def devip_webhook(request):
    if request.method != 'POST':
        return JsonResponse({'error': 'Method not allowed'}, status=405)

    signature = request.headers.get('x-devip-signature', '')
    raw_body = request.body
    secret = settings.DEVIP_WEBHOOK_SECRET.encode('utf-8')

    expected = hmac.new(secret, raw_body, hashlib.sha256).hexdigest()

    if not hmac.compare_digest(expected, signature):
        return JsonResponse({'message': 'Invalid signature'}, status=401)

    payload = json.loads(raw_body)

    if payload['event'] == 'charge.success':
        fulfill_order(payload['data']['reference'], payload['data']['amount'])

    return JsonResponse({'received': True})

Webhook Events

charge.successA wallet debit or bank transfer was completed successfully
charge.failedA debit attempt failed (insufficient funds, etc.)
transfer.receivedA bank transfer to a virtual account was received

Sandbox & Testing

Use the sandbox endpoint to simulate a completed bank transfer payment without any real money moving. This is useful for testing your webhook handler and order fulfillment logic.
bash
# Simulate a bank transfer payment (sandbox only)
curl -X POST https://api.devip.me/api/v1/transfers/simulate-pay/ORDER-REF-abc123
Calling this endpoint fires a real charge.success webhook to your registered webhook URL so you can test your end-to-end flow without a real transfer.

Test Credentials

Key
Value
client_id
devip_test_client_xxx
client_secret
devip_test_secret_xxx
webhook_secret
devip_test_whsec_xxx
test_account_id
DEVIP-TEST-001

Request your personal sandbox credentials from dev@devip.me.

SDKs & Libraries

Official SDKs are coming soon. In the meantime, the API is straightforward enough to integrate using any HTTP client.
Node.jsComing soon
PythonComing soon
PHP / LaravelComing soon

Quick Start — Node.js

javascript
// Install: npm install node-fetch (or use native fetch in Node 18+)

class DevipClient {
  constructor(clientId, clientSecret) {
    this.baseUrl = 'https://api.devip.me/api/v1';
    this.headers = {
      'X-Devip-Client-Id': clientId,
      'Authorization': `Bearer ${clientSecret}`,
      'Content-Type': 'application/json',
      'Accept': 'application/json',
    };
  }

  async getWalletBalance(accountId) {
    const res = await fetch(
      `${this.baseUrl}/wallet/balance?account_id=${accountId}`,
      { headers: this.headers }
    );
    return res.json();
  }

  async debitWallet(accountId, amount, reference) {
    const res = await fetch(`${this.baseUrl}/wallet/debit`, {
      method: 'POST',
      headers: this.headers,
      body: JSON.stringify({ account_id: accountId, amount, reference }),
    });
    return res.json();
  }

  async generateVirtualAccount(clientId, amount, reference) {
    const res = await fetch(`${this.baseUrl}/transfers/generate`, {
      method: 'POST',
      headers: this.headers,
      body: JSON.stringify({ client_id: clientId, amount, reference }),
    });
    return res.json();
  }
}

// Usage
const devip = new DevipClient(process.env.DEVIP_CLIENT_ID, process.env.DEVIP_CLIENT_SECRET);

const balance = await devip.getWalletBalance('DEVIP-12345');
console.log(balance.data.balance); // 25000.00

Ready to integrate?

Email us to get your API credentials and go live.