POST https://mybundlepay.com/ng/api/v2/virtual-card/topup

KEY USAGE POLICY

Important: Use your test_secret_key for sandbox testing. Switch to live_secret_key only when you are ready to go live.

  • Test calls return mock data and do not process real transactions.
  • Live calls top-up real virtual cards.
  • Ensure your server IP is whitelisted before calling live endpoints.

⚠️ Do not use live keys for testing — unauthorized IPs will be blocked.

HEADERS

Authorization * string

Send your {secret_key} as a Bearer token in the header.

Content-Type * application/json

All requests must use JSON body format.

IP WHITELISTING

Only authorized server IPs can access this endpoint. Whitelist your IP in MyBundlePay Dashboard.

BODY PARAMS

Parameter Required Description
amount Amount to top-up (USD). Minimum $1.
cardId Unique card ID to top up (retrieved from card creation response).
reference Unique reference for this transaction. Must be unique per top-up.

<?php

$curl = curl_init();

curl_setopt_array($curl, array(
  CURLOPT_URL => 'https://mybundlepay.com/ng/api/v2/virtual-card/topup',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => '',
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 0,
  CURLOPT_FOLLOWLOCATION => true,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => 'POST',
  CURLOPT_POSTFIELDS =>'{
    "amount": "1",
    "cardId": "xxxxx-a228-4ec2-9337-xxxxx",
    "reference": "MBP-TEST-amount006"
}',
  CURLOPT_HTTPHEADER => array(
    'Content-Type: application/json',
    'Authorization: Bearer {secret_key}'
  ),
));

$response = curl_exec($curl);
curl_close($curl);
echo $response;
?>

const axios = require('axios');

const data = {
  amount: "1",
  cardId: "xxxxx-a228-4ec2-9337-xxxxx",
  reference: "MBP-TEST-amount006"
};

axios.post("https://mybundlepay.com/ng/api/v2/virtual-card/topup", data, {
  headers: {
    "Content-Type": "application/json",
    "Authorization": "Bearer {secret_key}"
  }
})
.then(res => console.log(res.data))
.catch(err => console.error(err.response ? err.response.data : err.message));
✅ Success Response

{
  "status": "success",
  "message": "Card top-up initiated successfully. Get confirmation from webhook.",
  "data": {
    "provider_response": {
      "status": true,
      "message": "card topup in progress",
      "data": {
        "id": "xxxxxx-67f0-4fd0-aa7a-xxxxxxx",
        "type": "credit",
        "method": "topup",
        "cardId": "xxxxx-a228-4ec2-9337-xxxxx",
        "currency": "usd",
        "centAmount": "100",
        "status": "pending",
        "reference": "MBP-TEST-amount006",
        "amount": "1"
      }
    },
    "reference": "MBP-TEST-amount006",
    "amount": "1"
  },
  "mode": "test"
}
❌ Error Response (IP Not Authorized)

{
  "status": "failed",
  "code": "IP_NOT_AUTHORIZED",
  "message": "Your IP is not authorized for API access.",
  "ip": "54.86.50.139",
  "mode": "live"
}
❌ Error Response (Invalid Amount)

{
    "status": "failed",
    "code": "INVALID_AMOUNT",
    "message": "The minimum top-up amount is $1. Amount must be 1 or higher.",
    "errors": {
        "amount": [
            "The minimum top-up amount is 1."
        ]
    },
    "mode": "test"
}
❌ Error Response (Missing Params)

{
  "status": "failed",
  "message": "All body parameters are required: amount, cardId, reference.",
  "errors": {
    "amount": ["The amount field is required."],
    "cardId": ["The card id field is required."],
    "reference": ["The reference field is required."]
  },
  "mode": "test"
}
❌ Error Response (Duplicate Reference)

{
  "status": "failed",
  "message": "All body parameters are required: amount, cardId, reference.",
  "errors": {
    "reference": ["The reference has already been taken."]
  },
  "mode": "test"
}