# Introduction to the Bob Pay API The Bob Pay API enables seamless integration with Bob Pay, allowing you to start accepting payments quickly and easily. ## Getting started Before going live, you can use the fully functional sandbox environment to test and implement your API integration. This environment mirrors the behaviour of the production system but does not process real transactions, providing a safe space for testing. To begin, register for a [sandbox account](https://sandbox.bobpay.co.za/) to obtain your API keys, which can be found in the settings menu. The sandbox environment supports all available endpoints. **Sandbox base URL:** [https://api.sandbox.bobpay.co.za/v2/](https://api.sandbox.bobpay.co.za/v2/) ## Switching to production When you're ready to go live, you can transition from the sandbox to the production environment by updating the endpoint URLs and API keys. **Production base URL:** [https://api.bobpay.co.za/v2/](https://api.bobpay.co.za/v2/) ## Support If you need assistance at any stage of your integration, our support team is here to help. For technical issues, implementation questions, or general enquiries, please reach out to us at: Email: [support@bobpay.co.za](mailto:support@bobpay.co.za) --- # Login `POST /v2/login` **Base URLs:** - Sandbox: `https://api.sandbox.bobpay.co.za` - Production: `https://api.bobpay.co.za` ## Example request ```bash curl -X POST "https://api.sandbox.bobpay.co.za/v2/login" \ -H "content-type: application/json" \ -d '{ "email": "your-email@example.com", "password": "your-password" }' ``` Bob Pay uses **Bearer Token** authentication. After logging in, a JWT token is returned. Add this token as a header to all your API calls, e.g. `"Authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."` The token expires after 30 days, after which a new token needs to be generated. Log in with the email address and password of your [sandbox account](https://sandbox.bobpay.co.za/). --- # Generating a payment URL To direct your customer to the Bob Pay payment screen, you will need to create a payment URL and then navigate to that URL. - **Sandbox environment**: `https://sandbox.bobpay.co.za` - **Production environment**: `https://my.bobpay.co.za` ### Create the payment URL **Endpoint:** `POST /payments/intents/link` **Authentication:** Required - Bearer token (a JWT obtained via `/login`, or your API key sent as the bearer token) **Description:** Creates a payment intent and returns a unique payment URL. Your application should navigate to this URL to direct your customer to the Bob Pay payment page, where they can complete their payment using their preferred payment method. #### Request headers ```http Content-Type: application/json Authorization: Bearer YOUR_API_TOKEN ``` #### Request body | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `amount` | float | Yes | The payment amount in ZAR. Must be greater than zero. | | `email` | string | Conditional* | Customer's email address. | | `mobile_number` | string | Conditional* | Customer's mobile number in international format (e.g., +27821234567). | | `item_name` | string | No | Name of the product or service being purchased. | | `item_description` | string | No | Detailed description of the product or service. | | `custom_payment_id` | string | No | Your unique reference/identifier for this payment. If not provided, a UUID will be generated automatically. | | `notify_url` | string | No* | Webhook URL where payment status notifications will be sent. | | `success_url` | string | No* | URL to redirect the customer after a successful payment. | | `pending_url` | string | No* | URL to redirect the customer when the payment is pending. | | `cancel_url` | string | No* | URL to redirect the customer if they cancel the payment. | | `short_url` | boolean | No | If set to `true`, a shortened URL will also be generated and returned. Default: `false`. | \* **Note:** Either `email` or `mobile_number` must be provided. The callback URLs are optional, but strongly recommended: without a `notify_url` you will not receive payment notifications, and without the redirect URLs your customer will not be returned to your site after payment. #### Example request ```json { "amount": 1299.99, "email": "customer@example.com", "mobile_number": "+27821234567", "item_name": "Wireless Bluetooth Headphones", "item_description": "Premium noise-cancelling wireless headphones - Black", "custom_payment_id": "ORDER-2024-001", "notify_url": "https://yourwebsite.co.za/webhook/payment-notification", "success_url": "https://yourwebsite.co.za/payment/success", "pending_url": "https://yourwebsite.co.za/payment/pending", "cancel_url": "https://yourwebsite.co.za/payment/cancelled", "short_url": true } ``` #### Response **Success response (200 OK):** ```json { "url": "https://sandbox.bobpay.co.za/pay/ref/3WFFG", "short_url": "https://api.sandbox.bob.co.za/r/V4X7WG" } ``` | Field | Type | Description | | --- | --- | --- | | `url` | string | The full payment URL to redirect your customer to. | | `short_url` | string | The shortened payment URL. Equal to `url` unless `short_url: true` was requested. | #### Usage notes 1. **Navigation flow:** After receiving the payment URL in the response, your application should immediately redirect/navigate the user's browser to this URL. This typically happens during your checkout process. 2. **Webhook notifications:** Bob Pay will send POST requests to your `notify_url` with payment status updates. Ensure your webhook endpoint is publicly accessible and can handle POST requests. 3. **URL best practices:** - All callback URLs (notify_url, success_url, pending_url, cancel_url) must be valid URLs; we strongly recommend HTTPS - Use the `short_url` option if you need to store or display a shorter URL - Include query parameters in your callback URLs to track which payment they relate to 4. **Custom payment ID:** Use the `custom_payment_id` field to link the Bob Pay payment to your internal order/transaction system. This ID will be included in webhook notifications. #### Complete integration example ```javascript // Example using Node.js and Express const axios = require("axios"); const express = require("express"); const app = express(); app.post("/checkout", async (req, res) => { try { const orderDetails = req.body; // Create the payment link const response = await axios.post( "https://api.sandbox.bobpay.co.za/v2/payments/intents/link", { amount: orderDetails.totalAmount, email: orderDetails.customerEmail, mobile_number: orderDetails.customerMobile, item_name: orderDetails.productName, item_description: orderDetails.productDescription, custom_payment_id: orderDetails.orderId, notify_url: "https://yourwebsite.com/webhook/payment", success_url: `https://yourwebsite.com/order/${orderDetails.orderId}/success`, pending_url: `https://yourwebsite.com/order/${orderDetails.orderId}/pending`, cancel_url: `https://yourwebsite.com/order/${orderDetails.orderId}/cancelled`, short_url: false, }, { headers: { "Content-Type": "application/json", Authorization: `Bearer ${process.env.BOBPAY_API_TOKEN}`, }, } ); // Redirect the customer to Bob Pay payment page const paymentUrl = response.data.url; res.redirect(paymentUrl); } catch (error) { console.error( "Error creating payment link:", error.response?.data || error.message ); res.status(500).send("Payment initialisation failed"); } }); ``` ### Payment methods By default, when a customer visits the payment URL, they are presented with all available payment methods enabled for your merchant account. However, you can direct customers to a specific payment method by specifying the `payment_method` in the request body. Include the `payment_method` parameter when creating the payment link: ```json { "amount": 1299.99, "email": "customer@example.com", "item_name": "Wireless Bluetooth Headphones", "notify_url": "https://yourwebsite.co.za/webhook/payment-notification", "success_url": "https://yourwebsite.co.za/payment/success", "pending_url": "https://yourwebsite.co.za/payment/pending", "cancel_url": "https://yourwebsite.co.za/payment/cancelled", "payment_method": "credit-card" } ``` #### Available payment methods | Value | Description | | --- | --- | | `account-balance` | Pay using Bob Pay account balance | | `credit-card` | Credit or debit card payment (Visa, Mastercard, Amex, Diners) | | `apple-pay` | Apple Pay | | `google-pay` | Google Pay | | `instant-eft` | Real-time bank transfer via Instant EFT | | `manual-eft` | Upload proof of payment for manual EFT transfer | | `pay-shap` | PayShap instant payment method | | `capitec-pay` | Capitec Pay instant payment method | | `scan-to-pay` | Scan to Pay (QR code payment) | | `nedbank-direct-eft` | Nedbank Direct EFT instant payment method | | `absa-pay` | Absa Pay instant payment method | #### Important notes 1. **Payment method availability:** Not all payment methods may be available for your account. Payment methods are subject to: - Global payment method activation status - Merchant account-specific settings - Transaction amount limits per payment method - Account status and permissions 2. **Transaction limits:** Each payment method has a maximum transaction amount. If the payment amount exceeds the limit for a specified payment method, that method will be shown as inactive. 3. **Fallback behaviour:** If you specify a payment method that is not available or inactive, the customer will see it as disabled with an appropriate message. 4. **Multiple payment methods:** To allow customers to choose from all available payment methods, simply omit the `payment_method` parameter from the URL. ### Handling notifications When a payment is completed, Bob Pay sends a POST notification to the `notify_url` you specified when creating the payment intent. This webhook notification contains the complete payment details. #### Webhook request **Method:** POST **Content-Type:** application/json **Request body example:** ```json { "id": 12345, "uuid": "550e8400-e29b-41d4-a716-446655440000", "short_reference": "3J9QZ", "custom_payment_id": "ORDER-2024-001", "amount": 1299.99, "paid_amount": 1299.99, "total_paid_amount": 1299.99, "status": "paid", "payment_method": "instant-eft", "original_requested_payment_method": "instant-eft", "payment_id": 67890, "payment": { "id": 67890, "payment_method_id": 3, "payment_method": "instant-eft", "amount": 1299.99, "status": "success" }, "item_name": "Wireless Bluetooth Headphones", "item_description": "Premium noise-cancelling wireless headphones - Black", "recipient_account_code": "", "recipient_account_id": 100, "recipient_account": { "id": 100, "name": "Your Store", "account_code": "ABC123", "signup_name": "Your Store", "is_merchant": true }, "email": "customer@example.com", "mobile_number": "+27821234567", "from_bank": "FNB", "time_created": "2024-01-15T10:30:00Z", "is_test": false, "signature": "5d41402abc4b2a76b9719d911017c592", "notify_url": "https://yourwebsite.co.za/webhook/payment-notification", "success_url": "https://yourwebsite.co.za/payment/success", "pending_url": "https://yourwebsite.co.za/payment/pending", "cancel_url": "https://yourwebsite.co.za/payment/cancelled" } ``` The webhook URL will also include a query parameter: `?type=payment` **Notes:** - The payload may contain additional fields not shown in the example above. - The top-level `recipient_account_code` field is always empty; your account code is available in `recipient_account.account_code`. - A notification is also sent when a payment fails; the payload then has status `failed` and includes an `error_message` field. #### Security & validation To ensure the webhook is legitimate and the payment is valid, you must perform the following security checks: ##### 1. Verify source IP address All webhook requests originate from Bob Pay's static IP addresses. Verify that the request comes from one of these IPs: - **Sandbox environment:** `13.245.58.93` - **Production environment:** `13.246.100.25` ##### 2. Verify the signature The `signature` field in the webhook payload is an MD5 hash that you should verify to confirm the data hasn't been tampered with. **Signature calculation:** ```javascript // Example: Node.js signature verification const crypto = require("crypto"); // URL-encode a value the same way Bob Pay does (form encoding: spaces become "+") function queryEscape(value) { return encodeURIComponent(value) .replace(/%20/g, "+") .replace(/[!'()*]/g, (c) => "%" + c.charCodeAt(0).toString(16).toUpperCase()); } function verifySignature(webhookData, accountPassphrase, accountCode) { // The top-level recipient_account_code field in the payload is always empty. // The signature is calculated over your actual account code, so use your own // account code (also available in webhookData.recipient_account.account_code). const keyValuePairs = [ `recipient_account_code=${queryEscape(accountCode)}`, `custom_payment_id=${queryEscape(webhookData.custom_payment_id)}`, `email=${queryEscape(webhookData.email || "")}`, `mobile_number=${queryEscape(webhookData.mobile_number || "")}`, `amount=${webhookData.amount.toFixed(2)}`, `item_name=${queryEscape(webhookData.item_name || "")}`, `item_description=${queryEscape(webhookData.item_description || "")}`, `notify_url=${queryEscape(webhookData.notify_url)}`, `success_url=${queryEscape(webhookData.success_url)}`, `pending_url=${queryEscape(webhookData.pending_url)}`, `cancel_url=${queryEscape(webhookData.cancel_url)}`, ]; const signatureString = keyValuePairs.join("&") + `&passphrase=${accountPassphrase}`; const calculatedSignature = crypto .createHash("md5") .update(signatureString) .digest("hex"); return calculatedSignature === webhookData.signature; } ``` **Note:** The account passphrase is a secret key associated with your merchant account. This passphrase can be found in your account settings. ##### 3. Verify payment with Bob Pay After verifying the signature, you should confirm the payment's validity with Bob Pay by calling the validation endpoint with the full webhook payload. **Endpoint:** `POST /payments/intents/validate` **Request body:** Send the complete webhook payload you received. **Success response (200 OK):** The payment is valid and confirmed. **Error response:** A non-200 response is returned when the payment could not be verified or doesn't match. ##### 4. Verify the amount Ensure the `paid_amount` matches the expected amount for your order. Note that some payment methods may allow non-exact payments if enabled for your account. #### Responding to webhooks **Important:** You must return a `200 OK` HTTP status code to acknowledge receipt of the webhook. If Bob Pay doesn't receive a 200 OK response, it will retry sending the webhook. ```javascript // Example: Express.js webhook endpoint app.post("/webhook/payment-notification", async (req, res) => { try { const webhookData = req.body; // 1. Verify source IP const clientIp = req.ip || req.connection.remoteAddress; const allowedIPs = ["13.245.58.93", "13.246.100.25"]; // Sandbox and Production if (!allowedIPs.includes(clientIp)) { return res.status(403).send("Forbidden"); } // 2. Verify signature const isValidSignature = verifySignature( webhookData, YOUR_PASSPHRASE, YOUR_ACCOUNT_CODE ); if (!isValidSignature) { return res.status(400).send("Invalid signature"); } // 3. Verify amount const expectedAmount = await getOrderAmount(webhookData.custom_payment_id); if (webhookData.paid_amount !== expectedAmount) { return res.status(400).send("Amount mismatch"); } // 4. Validate with Bob Pay (send full webhook payload) const validationResponse = await axios.post( "https://api.sandbox.bobpay.co.za/v2/payments/intents/validate", webhookData, { headers: { Authorization: `Bearer ${process.env.BOBPAY_API_TOKEN}`, "Content-Type": "application/json", }, } ); // 5. Process the payment in your system await processOrder(webhookData.custom_payment_id, webhookData); // 6. Return 200 OK to acknowledge receipt res.status(200).send("OK"); } catch (error) { console.error("Webhook processing error:", error); // Still return 200 if you've successfully received and logged the webhook // to prevent retries for processing errors res.status(200).send("Received"); } }); ``` #### Webhook retry mechanism If Bob Pay doesn't receive a `200 OK` response from your webhook endpoint, it will automatically retry sending the notification using an exponential backoff strategy: **Retry schedule:** Bob Pay retries with increasing delays, up to 17 retries over approximately 4 days. **Best practices:** - Make your webhook endpoint idempotent (able to handle duplicate notifications safely) - Use the `uuid` or `custom_payment_id` to prevent processing the same payment multiple times - Store webhook receipts in your database to track which notifications you've already processed - Return `200 OK` as quickly as possible; perform time-consuming operations asynchronously #### Payment statuses | Status | Description | | --- | --- | | `unpaid` | Payment intent created but not yet paid | | `paid` | Payment completed successfully | | `failed` | Payment failed or was declined | | `canceled` | Payment was cancelled by the customer or system | | `refund_pending` | A refund has been initiated but not yet completed | | `refunded` | Payment has been fully refunded | | `partially_refunded` | Payment has been partially refunded | | `chargeback` | A chargeback was raised against the payment | | `deleted` | Payment intent was deleted | #### Troubleshooting 1. **Not receiving webhooks?** - Ensure your `notify_url` is publicly accessible (not localhost) - Verify your server accepts POST requests - Check your firewall allows traffic from Bob Pay's IP addresses - Ensure your endpoint returns a 200 OK status code 2. **Receiving duplicate webhooks?** - Implement idempotency using the `uuid` field - Check if your endpoint is returning non-200 status codes 3. **Signature validation failing?** - Use your own account code for `recipient_account_code` — the payload's top-level field is always empty (see `recipient_account.account_code`) - Ensure you're using the correct passphrase - Verify you're URL-encoding values correctly (spaces must be encoded as `+`, not `%20`) - Check that you're using the exact fields in the correct order - Ensure you're formatting the amount to 2 decimal places --- # Create payment link `POST /v2/payments/intents/link` **Base URLs:** - Sandbox: `https://api.sandbox.bobpay.co.za` - Production: `https://api.bobpay.co.za` **Authentication:** Bearer token required. Obtain one via `POST /login` (see the Authentication page). ## Example request ```bash curl -X POST "https://api.sandbox.bobpay.co.za/v2/payments/intents/link" \ -H "content-type: application/json" \ -H "Authorization: Bearer " \ -d '{ "amount": 499.99, "email": "customer@gmail.co.za", "mobile_number": "", "custom_payment_id": "478", "item_name": "Order 1452", "item_description": "Lego Star Wars", "notify_url": "https://api-sandbox.mystore.co.za/payment/bobpay", "success_url": "https://sandbox.mystore.co.za/accounts/payment-confirmation", "pending_url": "https://sandbox.mystore.co.za/accounts/payment-confirmation", "cancel_url": "https://sandbox.mystore.co.za/accounts/payment-cancel", "short_url": true }' ``` This endpoint allows you to generate a unique, secure payment link for a customer, which they can use to complete payment via supported methods. Optionally, a shortened URL can be returned for easier sharing. --- The payment is made to the account you are authenticated as, so no account code or signature is required. ### Request body parameters | Key | Description | Type | Required | | --- | --- | --- | --- | | amount | The payment amount in ZAR. Must be greater than zero. | float | Yes | | email | Payer's email address. | string | Conditional* | | mobile_number | Payer's mobile number in international format (e.g., +27821234567). | string | Conditional* | | custom_payment_id | Your unique reference for this payment. If not provided, a UUID is generated automatically. | string | No | | item_name | Name of the item being purchased. | string | No | | item_description | Description of the item. | string | No | | notify_url | URL to send payment status notifications to. | string | No | | success_url | URL to redirect to on successful payment. | string | No | | pending_url | URL to redirect to if payment is pending. | string | No | | cancel_url | URL to redirect to if payment is cancelled. | string | No | | payment_method | Direct the payer to a specific payment method (e.g., `credit-card`). Omit to show all available payment methods. | string | No | | short_url | Set to `true` to also return a shortened payment link. | boolean | No | \* **Note:** Either `email` or `mobile_number` must be provided. --- ### Response **Status code:** 200 OK **Content-Type:** application/json **Response body** ```json { "url": "https://sandbox.bobpay.co.za/pay/ref/3WFFG", "short_url": "https://api.sandbox.bob.co.za/r/V4X7WG" } ``` --- ### Response fields | Field | Description | Type | | --- | --- | --- | | url | The unique payment link for the transaction. | string | | short_url | Shortened version of the payment link. Equal to `url` unless `short_url: true` was requested. | string | --- # Validate `POST /v2/payments/intents/validate` **Base URLs:** - Sandbox: `https://api.sandbox.bobpay.co.za` - Production: `https://api.bobpay.co.za` **Authentication:** Bearer token required. Obtain one via `POST /login` (see the Authentication page). ## Example request ```bash curl -X POST "https://api.sandbox.bobpay.co.za/v2/payments/intents/validate" \ -H "content-type: application/json" \ -H "Authorization: Bearer " \ -d '{ "id": 12345, "uuid": "550e8400-e29b-41d4-a716-446655440000", "short_reference": "3J9QZ", "custom_payment_id": "ORDER-2024-001", "amount": 1299.99, "paid_amount": 1299.99, "total_paid_amount": 1299.99, "status": "paid", "payment_method": "instant-eft", "original_requested_payment_method": "instant-eft", "payment_id": 67890, "payment": { "id": 67890, "payment_method_id": 3, "payment_method": "instant-eft", "amount": 1299.99, "status": "success" }, "item_name": "Wireless Bluetooth Headphones", "item_description": "Premium noise-cancelling wireless headphones - Black", "recipient_account_code": "", "recipient_account_id": 100, "email": "customer@example.com", "mobile_number": "+27821234567", "from_bank": "FNB", "time_created": "2024-01-15T10:30:00Z", "is_test": false, "signature": "5d41402abc4b2a76b9719d911017c592", "notify_url": "https://yourwebsite.co.za/webhook/payment-notification", "success_url": "https://yourwebsite.co.za/payment/success", "pending_url": "https://yourwebsite.co.za/payment/pending", "cancel_url": "https://yourwebsite.co.za/payment/cancelled" }' ``` Validate a payment intent by sending the full webhook payload received from Bob Pay. --- # Payments The Payments API allows you to query and manage payments across your accounts with a variety of filters to tailor your searches. You can also export the query results into a CSV file for further analysis or record-keeping. This makes it easy to track transactions, monitor account activity, and integrate payment data into your financial systems. - **GET payment intents:** Retrieve all payment intent information or filter by parameters such as account, date, status, or custom references. This endpoint lets you track all initiated payment requests across your business. - **GET payment methods (public):** Retrieve the list of available and active payment methods for a specific account, enabling dynamic checkout and payment configuration in your application. - **POST refund payment:** Initiate a refund for a specific payment, allowing for seamless management of transaction reversals and enhanced customer service. - **POST validate payment intent signature:** Verify that a signature you generated for a payment intent matches the signature calculated by Bob Pay. This is useful for testing your signature generation before going live. - **POST payment intent URL shortener:** Generate a shortened version of a payment intent URL. This is useful for sharing concise payment links via email, SMS, or other channels. - **POST payment link:** Instantly generate a secure, ready-to-use payment link for your customers. Use this endpoint to simplify payment collection for invoices, manual billing, or one-time transactions. You may also request a shortened URL for convenient sharing via email, SMS, or social channels. These endpoints make it simple to create, track, update, and reconcile payments, giving you full control over the payment lifecycle from initiation through refund, while maintaining compliance and ease of integration. --- # Payment intent `GET /v2/payments/intents` **Base URLs:** - Sandbox: `https://api.sandbox.bobpay.co.za` - Production: `https://api.bobpay.co.za` **Authentication:** Bearer token required. Obtain one via `POST /login` (see the Authentication page). ## Example request ```bash curl -X GET "https://api.sandbox.bobpay.co.za/v2/payments/intents" \ -H "Authorization: Bearer " ``` This endpoint allows you to query and retrieve comprehensive information about payment intents across your accounts. With support for a variety of filters, you can refine your search to access specific payment details. This endpoint also provides an option to export the query results into a CSV file, facilitating further analysis and record-keeping. #### Available parameters | Key | Description | Type | Required | | --- | --- | --- | --- | | id | The ID of a payment intent. | integer | No | | statuses | One or more of the following statuses, passed as a JSON array (e.g. `statuses=["paid","unpaid"]`): paid, unpaid, canceled, refunded, partially_refunded, refund_pending, failed, deleted, chargeback. | array of string | No | | from_bank | The payer's bank. One of: standard-bank, fnb, nedbank, absa, capitec, investec, bank-zero, tyme-bank, discovery-bank, african-bank, old-mutual-bank. | string | No | | recipient_account_id | Pass in the recipient_account_id to get payment intents received. | integer | No | | account_id | Pass in account_id to get payment intents made. | integer | No | | search | Case-insensitive partial match on payment references. | string | No | | csv | Use true to export the results to a CSV file. The CSV is generated in the background and emailed to you. | boolean | No | | start_date | Start date for the time period (format: YYYY-MM-DD HH:MM:SS). | string (date/time) | No | | end_date | End date for the time period (format: YYYY-MM-DD HH:MM:SS). | string (date/time) | No | | limit | Limit the number of records returned. | integer | No | | offset | The number of records to skip (for pagination). | integer | No | | order | ASC / DESC | string | No | | order_by | Order by any field. | string | No | | include_retained_date | If true, includes the date until which funds are retained (`retained_until`) in the results. | boolean | No | #### Response example ```json { "payment_intents": [ { "id": 54370, "uuid": "550e8400-e29b-41d4-a716-446655440000", "short_reference": "3WFFG", "from_bank": "fnb", "custom_payment_id": "265", "notify_url": "https://yourwebsite.co.za/webhook/payment-notification", "success_url": "https://yourwebsite.co.za/payment/success", "pending_url": "https://yourwebsite.co.za/payment/pending", "cancel_url": "https://yourwebsite.co.za/payment/cancelled", "item_name": "Order 1452", "item_description": "Lego Star Wars", "amount": 200, "paid_amount": 200, "total_paid_amount": 200, "signature": "1eb8d2109cb744f0b6a63f168a75ac8e", "time_created": "2024-06-12T11:14:07.16374+02:00", "account_id": 208, "account": { "id": 208, "name": "Auto-generated account", "account_code": "AUT002", "signup_name": "Sandbox User", "is_merchant": false }, "account_code": null, "transacting_as_email": "customer@example.co.za", "status": "paid", "recipient_account_code": "", "recipient_account_id": 209, "recipient_account": { "id": 209, "name": "Bob Go", "account_code": "BOB001", "signup_name": "John Doe", "is_merchant": true }, "email": "customer@example.co.za", "is_test": false, "payment_method": "credit-card", "original_requested_payment_method": "credit-card", "payment_id": 37499, "payment": { "id": 37499, "payment_method_id": 167, "payment_method": "credit-card", "amount": 200, "status": "success" } } ], "count": 1 } ``` --- #### Response fields | Key | Description | Type | | --- | --- | --- | | payment_intents | Array of payment intent objects matching the query. | array | | count | Total number of payment intents matching the query. | integer | **Payment intent fields:** | Key | Description | Type | | --- | --- | --- | | id | Unique identifier for the payment intent. | integer | | uuid | Universally unique identifier for the payment intent. | string | | short_reference | Short reference or code for easy tracking/display. | string | | from_bank | The payer's bank, if known. | string | | custom_payment_id | Custom identifier for the payment (business reconciliation). | string | | notify_url | URL notified when the payment status changes. | string | | success_url | Redirect URL for successful payment. | string | | pending_url | Redirect URL for pending payment. | string | | cancel_url | Redirect URL for cancelled payment. | string | | item_name | Name of the item being purchased. | string | | item_description | Description of the item. | string | | amount | Amount of the payment intent. | float | | paid_amount | Amount paid for this intent. | float | | total_paid_amount | Total amount paid towards this intent. | float | | signature | Signature for this intent. | string | | time_created | Timestamp when the payment intent was created (ISO 8601 format). | string (datetime) | | account_id | ID of the payer's account. | integer | | account | The payer's account object (see below for nested fields). | object | | account_code | Code of the payer's account (always null; see `account.account_code`). | string | | transacting_as_email | The payer's email address (if acting on behalf of another). | string | | status | Payment intent status (`paid`, `unpaid`, `canceled`, `refunded`, `partially_refunded`, `refund_pending`, `failed`, `deleted`, `chargeback`). | string | | recipient_account_code | Code of the account receiving the payment (always empty; see `recipient_account.account_code`). | string | | recipient_account_id | ID of the recipient (payee) account. | integer | | recipient_account | The recipient's account object (see below for nested fields). | object | | mobile_number | Payer's mobile number. | string | | email | Payer's email address. | string | | is_test | Whether this payment intent is a test transaction. | boolean | | payment_method | The payment method used (e.g., credit-card, instant-eft). | string | | original_requested_payment_method | The payment method originally requested when the intent was created. | string | | payment_id | ID of the payment record associated with this intent. | integer | | payment | Object containing payment transaction details (see below for nested fields). | object | | retained_until | If the funds are retained, the date until which they are retained (only included when `include_retained_date=true`). | string (datetime) | **account / recipient_account nested fields:** | Key | Description | Type | | --- | --- | --- | | id | Account ID | integer | | name | Account name | string | | account_code | Unique code of the account | string | | signup_name | Signup name for the account | string | | is_merchant | Whether the account is a merchant | boolean | **payment nested fields:** | Key | Description | Type | | --- | --- | --- | | id | Unique identifier for the payment transaction. | integer | | payment_method_id | Internal ID referencing the specific payment method used. | integer | | payment_method | Name of the payment method used for the transaction. | string | | amount | The amount processed in this payment transaction. | float | | status | Status of the payment transaction (`success`, `pending`, `failed`, `cancelled`, `reversed`, `refunded`, `refund_pending`, `partially_refunded`, `chargeback`). | string | --- # Get public payment methods `GET /v2/payments/payment-methods/public` **Base URLs:** - Sandbox: `https://api.sandbox.bobpay.co.za` - Production: `https://api.bobpay.co.za` **Authentication:** Bearer token required. Obtain one via `POST /login` (see the Authentication page). ## Example request ```bash curl -X GET "https://api.sandbox.bobpay.co.za/v2/payments/payment-methods/public" \ -H "Authorization: Bearer " ``` This endpoint retrieves the list of payment methods available for a specific account, including their status and display order. ### Request **Method:** `GET` **URL:** `https://api.sandbox.bobpay.co.za/v2/payments/payment-methods/public` #### Query parameters | Key | Description | Type | Required | | --- | --- | --- | --- | | account_code | The unique code identifying the account. | string | Yes | --- ### Response **Status code:** `200 OK` **Content-Type:** `application/json` #### Response body ```json { "payment_methods": [ { "name": "absa-pay", "status": "inactive", "order": 8 }, { "name": "credit-card", "status": "active", "order": 1 }, { "name": "instant-eft", "status": "active", "order": 2 }, { "name": "account-balance", "status": "active", "order": 0 }, { "name": "pay-shap", "status": "active", "order": 4 }, { "name": "manual-eft", "status": "active", "order": 3 }, { "name": "capitec-pay", "status": "active", "order": 5 }, { "name": "scan-to-pay", "status": "active", "order": 6 }, { "name": "nedbank-direct-eft", "status": "active", "order": 7 } ] } ``` --- #### Response fields | Key | Description | Type | | --- | --- | --- | | payment_methods | Array of available payment method objects for the specified account. | array | | name | The unique name/identifier of the payment method. | string | | status | The activation status of the payment method. | string | | order | The display order for the payment method (lower number = higher priority). | integer | --- # Refund payment (payment reversal) `POST /v2/payments/reversal` **Base URLs:** - Sandbox: `https://api.sandbox.bobpay.co.za` - Production: `https://api.bobpay.co.za` **Authentication:** Bearer token required. Obtain one via `POST /login` (see the Authentication page). ## Example request ```bash curl -X POST "https://api.sandbox.bobpay.co.za/v2/payments/reversal" \ -H "content-type: application/json" \ -H "Authorization: Bearer " \ -d '{ "id": 72052 }' ``` This endpoint enables you to initiate a refund for a specific payment. It allows you to process payment reversals efficiently, ensuring that refunds are handled seamlessly. --- ### Request **Method:** POST **URL:** `https://api.sandbox.bobpay.co.za/v2/payments/reversal` --- #### Request body parameters | Key | Description | Type | Required | | --- | --- | --- | --- | | id | ID of the payment record to refund. | integer | Conditional* | | custom_payment_id | Your custom identifier for the payment to refund. | string | Conditional* | | payment_method_id | Payment method record ID, used together with `payment_method`. | integer | Conditional* | | payment_method | The payment method of the payment, used together with `payment_method_id`. | string | Conditional* | | reversal_amount | The amount to refund, for partial refunds. Must be greater than zero and may not exceed the payment amount. Defaults to the full payment amount. | float | No | | reverse_directly_to_bank | If true, the funds are reversed directly to the payer's bank instead of their Bob Pay account balance. Only supported for certain payment methods (e.g., card payments, Scan to Pay, Capitec Pay). Defaults to `true` for payment methods that support it; set to `false` to refund to the payer's Bob Pay account balance instead. | boolean | No | \* **Note:** Identify the payment using one of `id`, `custom_payment_id`, or `payment_method_id` together with `payment_method`. **Example:** ```json { "id": 72052 } ``` --- ### Response **Status code:** 200 OK **Content-Type:** application/json --- #### Response body ```json { "payment_method": { "id": 1186, "payment_intent_id": 114092, "payment_intent_reference": "3R3PW", "account_id": 377, "account": { "id": 377, "name": "Auto-generated account", "account_code": "AUT065", "signup_name": null, "is_merchant": false }, "recipient_account_id": 370, "recipient_account": { "id": 370, "name": "Bob shop", "account_code": "BOB010", "signup_name": "Sandbox User", "merchant_start_date": "2024-11-29T11:06:28.425924+02:00", "is_merchant": true }, "amount": 5, "status": "refunded", "transaction_date": "2025-07-17T11:47:23.321682+02:00", "type": "payment", "time_created": "2025-07-17T11:47:23.323953+02:00", "time_modified": "2025-07-17T11:47:39.619253+02:00", "custom_payment_id": "13840594", "jurisdiction": "Local", "card_association": "VISA", "is_debit_card": false }, "reversed_directly_to_bank": true } ``` --- #### Response fields | Key | Description | Type | | --- | --- | --- | | payment_method | Object with details about the reversed payment. | object | | payment_method.id | Unique payment record ID. | integer | | payment_method.payment_intent_id | Payment intent ID linked to this payment. | integer | | payment_method.payment_intent_reference | Short payment intent reference. | string | | payment_method.account_id | Originating account ID. | integer | | payment_method.account | Object with originating account details. | object | | payment_method.recipient_account_id | Recipient account ID. | integer | | payment_method.recipient_account | Object with recipient account details. | object | | payment_method.amount | Amount refunded. | float | | payment_method.status | Status of the refund (should be "refunded"). | string | | payment_method.transaction_date | Transaction date and time. | string (date) | | payment_method.type | Type of transaction (e.g., "payment"). | string | | payment_method.time_created | Time created. | string (date) | | payment_method.time_modified | Time last modified. | string (date) | | payment_method.custom_payment_id | Custom payment identifier. | string | | payment_method.jurisdiction | Payment jurisdiction. | string | | payment_method.card_association | Card association (e.g., VISA). | string | | payment_method.is_debit_card | True if debit card used. | boolean | | reversed_directly_to_bank | True if funds were reversed directly to bank. | boolean | **Note:** - The fields of the `payment_method` object depend on the payment method being refunded. The example above shows a card refund; fields such as `jurisdiction`, `card_association`, and `is_debit_card` are card-specific. - The response also contains deprecated `PaymentMethod` and `ReversedDirectlyToBank` keys (duplicates of `payment_method` and `reversed_directly_to_bank`). Ignore these; they will be removed in a future version. --- # Shorten url `POST /v2/payments/intents/shorten` **Base URLs:** - Sandbox: `https://api.sandbox.bobpay.co.za` - Production: `https://api.bobpay.co.za` **Authentication:** Bearer token required. Obtain one via `POST /login` (see the Authentication page). ## Example request ```bash curl -X POST "https://api.sandbox.bobpay.co.za/v2/payments/intents/shorten" \ -H "content-type: application/json" \ -H "Authorization: Bearer " \ -d '{ "url": "https://sandbox.bobpay.co.za/pay?amount=499.99&recipient_account_code=SAN001&signature=1eb8d2109cb744f0b6a63f168a75ac8e" }' ``` Generate a shortened version of a payment intent URL. Returns a short_url string. --- # Validate payment intent signature `POST /v2/payments/intents/signature` **Base URLs:** - Sandbox: `https://api.sandbox.bobpay.co.za` - Production: `https://api.bobpay.co.za` **Authentication:** Bearer token required. Obtain one via `POST /login` (see the Authentication page). ## Example request ```bash curl -X POST "https://api.sandbox.bobpay.co.za/v2/payments/intents/signature" \ -H "content-type: application/json" \ -H "Authorization: Bearer " \ -d '{ "recipient_account_code": "SAN001", "custom_payment_id": "478", "email": "customer@bob.co.za", "mobile_number": "", "amount": 499.99, "item_name": "Order 1452", "item_description": "Lego Star Wars", "notify_url": "https://api-sandbox.bobpay.co.za/payment/bobpay", "success_url": "https://sandbox.bobpay.co.za/accounts/payment-confirmation?provider=bobpay&id=478", "pending_url": "https://sandbox.bobpay.co.za/accounts/payment-confirmation?provider=bobpay&id=478", "cancel_url": "https://sandbox.bobpay.co.za/accounts/payment-cancel?provider=bobpay&id=478", "signature": "1eb8d2109cb744f0b6a63f168a75ac8e" }' ``` This endpoint checks whether a previously generated signature is valid for a payment intent. ### Request **Method:** `POST` **URL:** `https://api.sandbox.bobpay.co.za/v2/payments/intents/signature` #### Request body parameters | Key | Description | Type | Required | | --- | --- | --- | --- | | recipient_account_code | The code of the account to receive the payment. | string | Yes | | custom_payment_id | Custom identifier for the payment. | string | Yes | | email | Payer's email address. | string | Conditional* | | mobile_number | Payer's mobile number. | string | Conditional* | | amount | The payment amount. | float | Yes | | item_name | Name of the item being purchased. Included in the signature calculation, even when empty. | string | No | | item_description | Description of the item. | string | No | | notify_url | URL to send payment status notifications. | string | No | | success_url | URL to redirect to on successful payment. | string | No | | pending_url | URL to redirect to if payment is pending. | string | No | | cancel_url | URL to redirect to if payment is cancelled. | string | No | | signature | The signature previously generated for this payment intent. | string | Yes | \* **Note:** Either `email` or `mobile_number` must be provided. If the signature is valid, the payment intent is echoed back with a `200 OK` response. If the signature is invalid, a `400 Bad Request` error is returned. --- ### Response **Status code:** `200 OK` **Content-Type:** `application/json` #### Response body ```json { "id": 0, "uuid": "", "short_reference": "", "recipient_account_code": "SAN001", "email": "customer@bob.co.za", "custom_payment_id": "478", "notify_url": "https://api-sandbox.bobpay.co.za/payment/bobpay", "success_url": "https://sandbox.bobpay.co.za/accounts/payment-confirmation?provider=bobpay&id=478", "pending_url": "https://sandbox.bobpay.co.za/accounts/payment-confirmation?provider=bobpay&id=478", "cancel_url": "https://sandbox.bobpay.co.za/accounts/payment-cancel?provider=bobpay&id=478", "item_name": "Order 1452", "item_description": "Lego Star Wars", "amount": 499.99, "total_paid_amount": 0, "signature": "1eb8d2109cb744f0b6a63f168a75ac8e", "status": "", "payment_method": "", "payment_id": 0, "payment": { "id": 0, "payment_method_id": 0, "payment_method": "", "amount": 0, "status": "" }, "is_test": false, "time_created": null, "time_modified": null } ``` --- #### Response fields | Field | Description | Type | | --- | --- | --- | | id | Unique identifier for the payment intent. | integer | | uuid | Universally unique identifier for the payment intent. | string | | short_reference | Short reference or code for easy tracking/display. | string | | recipient_account_code | Code of the account receiving the payment. | string | | email | Payer's email address. | string | | account_id | Unique identifier of the account initiating the payment. | integer | | custom_payment_id | Your custom reference for the payment. | string | | notify_url | URL to notify when payment status changes. | string | | success_url | Redirect URL for successful payment. | string | | pending_url | Redirect URL for pending payment. | string | | cancel_url | Redirect URL for cancelled payment. | string | | item_name | Name of the item being purchased. | string | | item_description | Description of the item. | string | | amount | Amount of the payment intent. | float | | total_paid_amount | Amount paid so far towards this intent. | float | | signature | Signature used for this intent (should match request). | string | | status | Current status of the payment intent (`canceled`, `chargeback`, `deleted`, `failed`, `paid`, `partially_refunded`, `refund_pending`, `refunded`, `unpaid`). | string | | payment_method | The actual method used for the payment. | string | | payment_id | Internal system ID of the payment record associated with this intent. | integer | | payment | Object containing payment transaction details. | object | | is_test | Boolean flag indicating if this payment intent is a test transaction. | boolean | | time_created | Timestamp of when the payment intent was created. | string (datetime) | | time_modified | Timestamp of the last modification to the payment intent. | string (datetime) | | payment.id | Unique identifier for the actual payment transaction. | integer | | payment.payment_method_id | Internal ID referencing the specific payment method used. | integer | | payment.payment_method | Name of the payment method used for the transaction. | string | | payment.amount | The amount processed in this payment transaction. | float | | payment.status | Status of the payment transaction (`cancelled`, `chargeback`, `failed`, `partially_refunded`, `pending`, `refund_pending`, `refunded`, `reversed`, `success`). | string | --- # Payouts The Payouts API allows you to manage and query payout requests across your accounts. - **GET payout requests:** Retrieve detailed information about payout requests, with the ability to apply various filters to refine your search. This helps in monitoring and managing outgoing payments efficiently. - **GET payout schedules:** Retrieve all scheduled payout configurations for your account. You can filter by payout frequency (daily, weekly, or monthly) or account ID. This endpoint enables you to view when and how often payouts are processed, the minimum payout amounts, and the configured payout days. These endpoints ensure you have full visibility and control over your payout activities. --- # Get payout requests `GET /v2/payout-requests` **Base URLs:** - Sandbox: `https://api.sandbox.bobpay.co.za` - Production: `https://api.bobpay.co.za` **Authentication:** Bearer token required. Obtain one via `POST /login` (see the Authentication page). ## Example request ```bash curl -X GET "https://api.sandbox.bobpay.co.za/v2/payout-requests" \ -H "Authorization: Bearer " ``` This endpoint allows you to retrieve detailed information about payout requests for your accounts. You can filter requests by status, creation date, and more, enabling you to efficiently monitor and manage outgoing payments. --- ### Request **Method:** GET **URL:** `https://api.sandbox.bobpay.co.za/v2/payout-requests` --- #### Query parameters | Key | Description | Type | Required | | --- | --- | --- | --- | | id | The ID of a payout request record. | integer | No | | account_id | The ID of the account whose payout requests are requested. | integer | No | | status | The payout request status. One of: pending, paid, payment-failed, rejected, cancelled. | string | No | | start_date | Start date for the time period (format: YYYY-MM-DD HH:MM:SS). | string (date/time) | No | | end_date | End date for the time period (format: YYYY-MM-DD HH:MM:SS). | string (date/time) | No | | csv | Use true to export the results to a CSV file. The CSV is generated in the background and emailed to you. | boolean | No | | order | Sort order: ASC / DESC. | string | No | | order_by | Order by any field. | string | No | | offset | The number of records to skip (for pagination). | integer | No | | limit | Limit the number of records returned. | integer | No | --- ### Response **Status code:** 200 OK **Content-Type:** application/json **Response body** ```json { "payouts": [ { "id": 69, "account_id": 335, "account": { "id": 335, "name": "Test Account", "account_code": "TA001", "signup_name": null, "merchant_start_date": "2025-04-10T09:08:04.303705+02:00", "is_merchant": true }, "account_billing_info": { "id": 237, "account_id": 335, "bank_details_bank": "Absa", "bank_details_branch_code": "000123", "bank_details_account_number": "12343545", "bank_account_type": "Cheque / current" }, "amount": 63992.61, "status": "pending", "payout_rejected_reason": null, "payout_date": null, "transaction_id": 163333, "banking_details": { "bank": "Absa", "branch_code": "000123", "account_number": "12343545", "account_type": "Cheque / current" }, "time_created": "2025-07-21T04:00:43.061748+02:00", "time_modified": "2025-07-21T04:00:43.061749+02:00", "from_bank": "fnb", "payment_reference": "", "total_note_count": 0 } ], "count": 1 } ``` --- ### Response fields | Field | Description | Type | | --- | --- | --- | | payouts | List of payout request objects | array | | payouts[].id | Unique identifier for the payout request | integer | | payouts[].account_id | The ID of the account requesting payout | integer | | payouts[].account | Account information | object | | payouts[].account_billing_info | Billing/banking info of the account | object | | payouts[].amount | The payout amount | float | | payouts[].status | Status of the payout request (`pending`, `paid`, `payment-failed`, `rejected`, `cancelled`) | string | | payouts[].payout_rejected_reason | Reason for payout rejection | string | | payouts[].payout_date | Date the payout was processed | string (datetime) | | payouts[].transaction_id | Related transaction ID | integer | | payouts[].banking_details | Destination banking details | object | | payouts[].time_created | Timestamp when payout request was created | string (datetime) | | payouts[].time_modified | Timestamp when payout request was last updated | string (datetime) | | payouts[].from_bank | Name or code of the originating bank | string | | payouts[].payment_reference | Payment reference | string | | payouts[].total_note_count | Number of notes attached to the payout request | integer | | count | Total number of payout requests matching your query | integer | **Note:** Payout requests that are still being processed internally are always returned with status `pending`; filtering by `status=pending` includes them. --- # Get scheduled payouts `GET /v2/payout-requests/schedule` **Base URLs:** - Sandbox: `https://api.sandbox.bobpay.co.za` - Production: `https://api.bobpay.co.za` **Authentication:** Bearer token required. Obtain one via `POST /login` (see the Authentication page). ## Example request ```bash curl -X GET "https://api.sandbox.bobpay.co.za/v2/payout-requests/schedule" \ -H "Authorization: Bearer " ``` This endpoint retrieves the automated payout schedules configured for your account, including frequency, minimum payout amounts, and payout days. --- ### Request **Method:** GET **URL:** `https://api.sandbox.bobpay.co.za/v2/payout-requests/schedule` --- #### Query parameters | Key | Description | Type | Required | | --- | --- | --- | --- | | account_id | The unique identifier of the account whose payout schedules are requested. | integer | No | | payout_frequency | Filter by payout frequency (daily, weekly, or monthly). | string | No | --- ### Response **Status code:** 200 OK **Content-Type:** application/json **Response body** ```json { "payout_request_schedules": [ { "id": 13, "account_id": 665, "payout_frequency": "monthly", "minimum_payout_amount": 10, "payout_frequency_day": 1, "time_created": "2025-03-20T15:16:31.044149+02:00", "time_modified": "0001-01-01T00:00:00Z", "modified_by": 602 } ], "count": 1 } ``` --- ### Response fields | Key | Description | Type | | --- | --- | --- | | payout_request_schedules | Array of payout schedule objects matching the query parameters. | array | | id | Unique identifier for the payout schedule. | integer | | account_id | The account associated with the payout schedule. | integer | | payout_frequency | Frequency of the scheduled payouts (`daily`, `weekly`, or `monthly`). | string | | minimum_payout_amount | The minimum amount required to trigger a payout. | number | | payout_frequency_day | The day on which the payout is scheduled (for weekly: 1 = Monday to 5 = Friday; for monthly: the day of the month). | integer | | time_created | Timestamp when the payout schedule was created. | string (datetime) | | time_modified | Timestamp when the payout schedule was last modified. | string (datetime) | | modified_by | The user ID who last modified the schedule. | integer | | count | Total number of payout schedules matching the query. | integer | **Note:** The `payout_frequency_day` field's meaning depends on the frequency: - For **weekly**, it's the day of the week: 1 (Monday) to 5 (Friday). - For **monthly**, it's the calendar day of the month. - For **daily**, it is not used. --- # Account settings The Account Settings API allows you to query specific configuration settings for your accounts. - **GET account payment methods:** Retrieve the available payment methods linked to your account by querying the _account_payment_methods_ setting. This enables you to view the payment methods that are active on your account. - **GET payout fee settings:** Access detailed information about payout fee configurations by querying the _payout_fee_settings_ setting. This helps you understand the fees applied to payout transactions for your account. These endpoints ensure you have full visibility over your account's payment methods and payout fee structures. --- # Get account payment method settings `GET /v2/accounts/settings` **Base URLs:** - Sandbox: `https://api.sandbox.bobpay.co.za` - Production: `https://api.bobpay.co.za` **Authentication:** Bearer token required. Obtain one via `POST /login` (see the Authentication page). ## Example request ```bash curl -X GET "https://api.sandbox.bobpay.co.za/v2/accounts/settings" \ -H "Authorization: Bearer " ``` This endpoint retrieves the payment method settings for a specified account. --- ### Request **Method:** GET **URL:** `https://api.sandbox.bobpay.co.za/v2/accounts/settings` --- #### Query parameters | Key | Description | Type | Required | | --- | --- | --- | --- | | for_account_id | The unique identifier of the account for which the payment method settings are being requested. Defaults to your own account; merchant users can only access their own account's settings. | integer | No | | setting | The specific setting type to retrieve. For this endpoint, it should be set to `account_payment_methods`. If omitted, all settings are returned. | string | No | --- ### Response **Status code:** 200 OK **Content-Type:** application/json **Response body example:** ```json { "account_settings": [ { "id": 350, "account_id": 259, "setting": "account_payment_methods", "value": "{\"absa-pay\": {\"enabled\": true, \"approved\": true, \"retention_days\": 0, \"max_transaction_amount\": 200000}, ...}", "time_created": "2025-02-24T10:28:53.812911+02:00", "time_modified": "2025-05-28T09:03:06.234595+02:00", "is_public": true } ], "count": 1 } ``` --- ### Response fields | Key | Description | Type | | --- | --- | --- | | account_settings | An array containing the settings related to the account payment methods. | array of objects | | id | The unique identifier for the setting. | integer | | account_id | The identifier of the account associated with the setting. | integer | | setting | The name of the setting. | string | | value | The value assigned to the setting (JSON string, see note below). | string | | time_created | The timestamp when the setting was created. | string (datetime) | | time_modified | The timestamp when the setting was last modified. | string (datetime) | | is_public | A boolean indicating if the setting is public. | boolean | | count | The total number of settings returned in the response. | integer | **Note:** The `value` field contains a JSON string that describes the available payment methods and their settings (such as `enabled`, `approved`, `retention_days`, and `max_transaction_amount` for each method). --- # Get account payout fee settings `GET /v2/accounts/settings` **Base URLs:** - Sandbox: `https://api.sandbox.bobpay.co.za` - Production: `https://api.bobpay.co.za` **Authentication:** Bearer token required. Obtain one via `POST /login` (see the Authentication page). ## Example request ```bash curl -X GET "https://api.sandbox.bobpay.co.za/v2/accounts/settings" \ -H "Authorization: Bearer " ``` This endpoint retrieves the payout fee settings for a specified account. --- ### Request **Method:** GET **URL:** `https://api.sandbox.bobpay.co.za/v2/accounts/settings` --- #### Query parameters | Key | Description | Type | Required | | --- | --- | --- | --- | | for_account_id | The unique identifier of the account for which the payout fee settings are being requested. Defaults to your own account; merchant users can only access their own account's settings. | integer | No | | setting | The specific setting type to retrieve. For this endpoint, it should be set to `payout_fee_settings`. If omitted, all settings are returned. | string | No | --- ### Response **Status code:** 200 OK **Content-Type:** application/json **Response body example:** ```json { "account_settings": [ { "id": 371, "account_id": 259, "setting": "payout_fee_settings", "value": "{\"payout_fee_threshold_amount\":20000,\"payout_flat_fee\":5}", "time_created": "2025-03-06T08:52:21.551161+02:00", "is_public": false } ], "count": 1 } ``` --- ### Response fields | Key | Description | Type | | --- | --- | --- | | account_settings | An array containing the settings related to the account payout fees. | array of objects | | id | The unique identifier for the setting. | integer | | account_id | The identifier of the account associated with the setting. | integer | | setting | The name of the setting (in this case, payout_fee_settings). | string | | value | The value assigned to the setting (JSON string, see note below). | string | | time_created | The timestamp when the setting was created. | string (datetime) | | time_modified | The timestamp when the setting was last modified. | string (datetime) | | is_public | A boolean indicating if the setting is public. | boolean | | count | The total number of settings returned in the response. | integer | **Note:** The `value` field contains a JSON string with the payout fee configuration: ```json { "payout_fee_threshold_amount": 20000, "payout_flat_fee": 5 } ``` - **payout_fee_threshold_amount** – payouts below this amount incur the flat fee; payouts of this amount or more are free. - **payout_flat_fee** – the flat fee applied to payouts below the threshold. --- # Billing The Billing API allows you to retrieve comprehensive billing documents and data related to your account, enabling streamlined financial reconciliation and transparency. - **GET invoices:** Retrieve detailed invoice records for your account by specifying invoice IDs, account ID, or filtering by status. This endpoint allows you to access invoice amounts, payment statuses, billing items, and even download invoices as PDF or CSV files for record-keeping. - **GET credit notes:** Access credit notes issued to your account by querying specific credit note IDs or account IDs. This enables you to review adjustments or refunds made to previous invoices, along with itemised credit details. Downloadable PDF and CSV formats are also supported. - **GET transactions:** Query and retrieve detailed information about transactions across your accounts, with support for various filters to refine your search. You can also export the results into a CSV file for further analysis or record-keeping. - **GET account statements:** Obtain account statements for a specified date range, providing a complete summary of account balances, transactions, and billing activity. Statements can be downloaded as PDF documents for auditing or financial review. These endpoints ensure you have complete visibility and control over all aspects of your account billing, covering issued invoices, adjustments via credit notes, and overall account statement history. --- # Get credit notes `GET /v2/billing/credit-notes` **Base URLs:** - Sandbox: `https://api.sandbox.bobpay.co.za` - Production: `https://api.bobpay.co.za` **Authentication:** Bearer token required. Obtain one via `POST /login` (see the Authentication page). ## Example request ```bash curl -X GET "https://api.sandbox.bobpay.co.za/v2/billing/credit-notes" \ -H "Authorization: Bearer " ``` This endpoint retrieves credit note data for specified credit note IDs, with options for PDF or CSV export. --- ### Request **Method:** GET **URL:** `https://api.sandbox.bobpay.co.za/v2/billing/credit-notes` --- #### Query parameters | Key | Description | Type | Required | | --- | --- | --- | --- | | id | Filter for a specific credit note ID. | integer | Conditional (if pdf=true) | | ids | A comma-separated list or array of credit note IDs to retrieve. | integer or array | No | | account_id | The unique identifier of the account whose credit notes are being requested. | integer | No | | pdf | Boolean flag to return a PDF download link instead of the credit note data. Only takes effect when `id` is specified. | boolean | No | | csv | Boolean flag. If true, the CSV is generated in the background and emailed to the authenticated user. | boolean | No | --- ### Response **Status code:** 200 OK **Content-Type:** application/json --- #### Response body (credit notes array) ```json { "credit_notes": [ { "id": 531, "account_id": 399, "credit_note_date": "2025-05-31T23:59:57.999999+02:00", "sub_total": 87000, "vat": 13050, "vat_percentage": 15, "total_amount": 100050, "time_created": "2025-06-01T00:03:38.162093+02:00", "time_modified": null, "metadata": {}, "credit_note_items": [], "validated": "" } ], "count": 1 } ``` --- #### Response body (download PDF URL) ```json { "credit_notes": null, "download_url": { "url": "https://payments-backend-dev-infra-billing.s3.af-south-1.amazonaws.com/pdfs/credit_note_531.pdf?...", "filename": "pdfs/credit_note_531.pdf", "bucket": "payments-backend-dev-infra-billing", "file_size": 56137 }, "count": 1 } ``` --- ### Response fields | Key | Description | Type | | --- | --- | --- | | credit_notes | An array containing the retrieved credit note objects, or null when a PDF is requested. | array of objects | | id | The unique identifier for the credit note. | integer | | account_id | The account associated with the credit note. | integer | | credit_note_date | The date the credit note was issued. | string (datetime) | | sub_total | The subtotal amount before VAT/tax. | float | | vat | The VAT amount credited. | float | | vat_percentage | The VAT percentage rate applied. | float | | total_amount | The total amount of the credit note (including VAT, if applicable). | float | | time_created | Timestamp when the credit note was created. | string (datetime) | | time_modified | Timestamp when the credit note was last modified. | string (datetime) | | metadata | Object containing additional information (issuer details, billing info, etc.). | object | | credit_note_items | Array of items credited in the credit note. | array of objects | | validated | Additional validation status or information. | string | | download_url | Object containing download link and metadata if pdf=true. | object | | count | The total number of credit notes or downloads returned in the response. | integer | --- **Note:** - The `metadata` object includes issuer, account, and billing info, while `credit_note_items` lists each credited item and its details. - When requesting a PDF (`pdf=true`), the `download_url` object is returned in place of the credit notes array. --- # Get invoices `GET /v2/billing/invoices` **Base URLs:** - Sandbox: `https://api.sandbox.bobpay.co.za` - Production: `https://api.bobpay.co.za` **Authentication:** Bearer token required. Obtain one via `POST /login` (see the Authentication page). ## Example request ```bash curl -X GET "https://api.sandbox.bobpay.co.za/v2/billing/invoices" \ -H "Authorization: Bearer " ``` This endpoint retrieves invoice data for specified invoice IDs, with options for PDF/CSV export and filtering by status. --- ### Request **Method:** GET **URL:** `https://api.sandbox.bobpay.co.za/v2/billing/invoices` --- #### Query parameters | Key | Description | Type | Required | | --- | --- | --- | --- | | id | Filter for a specific invoice ID. | integer | Conditional (if pdf=true) | | ids | A comma-separated list or array of invoice IDs to retrieve. | integer or array | No | | account_id | The unique identifier of the account whose invoices are being requested. | integer | No | | pdf | Boolean flag to return a PDF download link instead of the invoice data. Only takes effect when `id` is specified. | boolean | No | | start_date | Start date for the time period (format: YYYY-MM-DD HH:MM:SS). | string (date/time) | No | | end_date | End date for the time period (format: YYYY-MM-DD HH:MM:SS). | string (date/time) | No | | status | Filter invoices by their payment status (paid, partially-paid, unpaid). | string | No | | csv | Boolean flag. If true, the CSV is generated in the background and emailed to the authenticated user. | boolean | No | --- ### Response **Status code:** 200 OK **Content-Type:** application/json --- #### Response body (invoices array) ```json { "invoices": [ { "id": 108, "account_id": 3, "invoice_date": "2022-10-29T00:00:00+02:00", "sub_total": 0.09, "vat": 0, "vat_percentage": 15, "total_amount": 0.09, "outstanding_amount": 0, "status": "paid", "time_created": "2022-10-29T00:02:45.346762+02:00", "time_modified": "2022-10-29T00:02:49.811339+02:00", "metadata": {}, "invoice_items": [], "payment_date": "2022-10-29T00:02:49.811341+02:00", "validated": "" } ], "count": 1 } ``` --- #### Response body (download PDF URL) ```json { "invoices": null, "download_url": { "url": "https://payments-backend-dev-infra-billing.s3.af-south-1.amazonaws.com/pdfs/invoice_108.pdf?...", "filename": "pdfs/invoice_108.pdf", "bucket": "payments-backend-dev-infra-billing", "file_size": 57925 }, "count": 1 } ``` --- ### Response fields | Key | Description | Type | | --- | --- | --- | | invoices | An array containing the retrieved invoice objects, or null when a PDF is requested. | array of objects | | id | The unique identifier for the invoice. | integer | | account_id | The account associated with the invoice. | integer | | invoice_date | The date the invoice was issued. | string (datetime) | | sub_total | The subtotal amount before VAT/tax. | float | | vat | The VAT amount charged. | float | | vat_percentage | The VAT percentage rate applied. | float | | total_amount | The total amount of the invoice. | float | | outstanding_amount | The outstanding (unpaid) amount on the invoice. | float | | status | The payment status of the invoice (`paid`, `partially-paid`, `unpaid`). | string | | time_created | Timestamp when the invoice was created. | string (datetime) | | time_modified | Timestamp when the invoice was last modified. | string (datetime) | | metadata | Object containing additional information (issuer details, billing info, etc). | object | | invoice_items | Array of items included in the invoice. | array of objects | | payment_date | The date and time the invoice was paid. | string (datetime) | | validated | Additional validation status or information. | string | | download_url | Object containing download link and metadata if pdf=true. | object | | count | The total number of invoices returned in the response. | integer | --- **Note:** - The `metadata` object contains issuer, account, and billing info, while `invoice_items` lists each billed item and its details. - When requesting a PDF (`pdf=true`), the `download_url` object is returned in place of the invoices array. --- # Get billing statement `GET /v2/billing/statements` **Base URLs:** - Sandbox: `https://api.sandbox.bobpay.co.za` - Production: `https://api.bobpay.co.za` **Authentication:** Bearer token required. Obtain one via `POST /login` (see the Authentication page). ## Example request ```bash curl -X GET "https://api.sandbox.bobpay.co.za/v2/billing/statements" \ -H "Authorization: Bearer " ``` This endpoint retrieves an account statement for a specified period, with an option to download as PDF. --- ### Request **Method:** GET **URL:** `https://api.sandbox.bobpay.co.za/v2/billing/statements` --- #### Query parameters | Key | Description | Type | Required | | --- | --- | --- | --- | | pdf | Boolean flag to return a PDF download link for the statement instead of the statement data. | boolean | No | | start_date | The start date for the statement period (format: YYYY-MM-DD HH:MM:SS). | string (date/time) | No | | end_date | The end date for the statement period (format: YYYY-MM-DD HH:MM:SS). | string (date/time) | No | | account_id | The unique identifier of the account whose statement is being requested. | integer | Yes | **Note:** Very large statements cannot be returned directly as a PDF. In that case, the PDF is generated in the background and emailed to the authenticated user, and the response is `{"queued": "true", "email": ""}` instead of a download link. --- ### Response **Status code:** 200 OK **Content-Type:** application/json --- #### Response body (statement data) ```json { "statement": { "from_date": "2023-10-01T00:00:00Z", "to_date": "2023-10-31T00:00:00Z", "pending": 0, "closing_balance": 0, "issuer_details": {}, "account": {}, "account_billing_info": {}, "balance_brought_forward": {}, "transactions": [] } } ``` --- #### Response body (PDF download URL) ```json { "download_url": { "url": "https://payments-backend-dev-infra-billing.s3.af-south-1.amazonaws.com/pdfs/statement_335.pdf?...", "filename": "pdfs/statement_335.pdf", "bucket": "payments-backend-dev-infra-billing", "file_size": 45600 } } ``` --- ### Response fields | Key | Description | Type | | --- | --- | --- | | statement | Object containing the statement data for the specified period. | object | | from_date | The start date of the statement period. | string (datetime) | | to_date | The end date of the statement period. | string (datetime) | | pending | The amount of pending transactions. | float | | closing_balance | The closing balance at the end of the period. | float | | issuer_details | Object with the issuer's details (name, address, contact info, etc.). | object | | account | Object with account details. | object | | account_billing_info | Object with account billing details. | object | | balance_brought_forward | Object describing the balance carried over into the period. | object | | transactions | Array of statement transactions (may be null if no transactions). | array of objects | | download_url | Object containing PDF download info if pdf=true is used. | object | --- **Note:** When `pdf=true` is used, the response will contain a `download_url` object instead of statement details. --- # Transactions `GET /v2/billing/transactions` **Base URLs:** - Sandbox: `https://api.sandbox.bobpay.co.za` - Production: `https://api.bobpay.co.za` **Authentication:** Bearer token required. Obtain one via `POST /login` (see the Authentication page). ## Example request ```bash curl -X GET "https://api.sandbox.bobpay.co.za/v2/billing/transactions" \ -H "Authorization: Bearer " ``` This endpoint allows you to query and retrieve detailed information about transactions across your accounts. With the ability to apply various filters, you can refine your search to find specific transactions easily. Additionally, you can export the results into a CSV file for further analysis or record-keeping. --- ### Request **Method:** GET **URL:** `https://api.sandbox.bobpay.co.za/v2/billing/transactions` --- #### Query parameters | Key | Description | Type | Required | | --- | --- | --- | --- | | type | One or more of the following types: payment-received, payment-received-reversal, payment-debit, payment-debit-reversal, payment-credit, payment-credit-reversal, payment-refund, payment-refund-reversal, fee, fee-reversal, payout, payout-reversal, admin-debit, admin-debit-reversal, admin-credit, admin-credit-reversal, promotional-credit, promotional-credit-reversal, promotional-debit, promotional-debit-reversal, balance-adjustment-credit, balance-adjustment-debit, bad-debt-write-off, bad-debt-write-off-reversal, chargeback, chargeback-reversal | array of string | No | | search | Case-insensitive partial match on transactions | string | No | | start_date | Start date for the time period (format: YYYY-MM-DD HH:MM:SS) | string (date/time) | No | | end_date | End date for the time period (format: YYYY-MM-DD HH:MM:SS) | string (date/time) | No | | csv | Use true to export the results into a CSV file. The CSV is generated in the background and emailed to you. | boolean | No | | order | ASC / DESC | string | No | | order_by | Order by any field | string | No | | offset | The number of records to skip | integer | No | | limit | Limit the number of records returned | integer | No | --- ### Response **Status code:** 200 OK **Content-Type:** application/json **Response body example:** ```json { "billing_transactions": [ { "id": 65473, "transaction_date": "2024-06-18T10:14:43.010441+02:00", "type": "payment-debit", "description": "reversal-pay-shap-36625", "amount": -200, "amount_excl_vat": -200, "vat": 0, "doc_number": "", "account_id": 259, "account_code": "BOB009", "created_by": 246, "time_created": "2024-06-18T10:14:43.00013+02:00", "time_modified": "2024-06-18T10:14:43.01135+02:00", "modified_by": 246, "modified_by_user": { "id": 246, "name": "Jane Smith", "email": "jane@example.co.za" }, "invoice_id": null, "credit_note_id": null, "payment_intent_id": 54363, "payment_intent": { "id": 54363, "short_reference": "3RLH7", "payment_method": "pay-shap", "custom_payment_id": "99999", "email": "customer@example.co.za", "item_name": "Order 1452", "item_description": "Lego Star Wars" } } ], "count": 4 } ``` --- ### Response fields | Key | Description | Type | | --- | --- | --- | | billing_transactions | Array containing the transaction objects matching the query. | array of objects | | id | Unique identifier for the transaction. | integer | | transaction_date | Date and time of the transaction. | string (datetime) | | type | Transaction type (e.g., payment-debit, payment-credit, fee, payout, etc.). | string | | description | Description of the transaction. | string | | amount | Amount of the transaction (negative for debits). | float | | amount_excl_vat | Amount excluding VAT (signed the same way as `amount`). | float | | vat | VAT amount. | float | | doc_number | Associated document number (e.g., invoice or credit note number). | string | | account_id | Account associated with the transaction. | integer | | account_code | Code of the account associated with the transaction. | string | | created_by | User ID who created the transaction. | integer | | time_created | Timestamp when the transaction was created. | string (datetime) | | time_modified | Timestamp when the transaction was last modified. | string (datetime) | | modified_by | User ID who last modified the transaction. | integer | | modified_by_user | User object with details of the last modifier. | object | | invoice_id | Associated invoice ID. | integer | | credit_note_id | Associated credit note ID. | integer | | payment_intent_id | Payment intent ID associated with this transaction, if applicable. | integer | | payment_intent | Payment intent object with additional details. | object | | retained_until | If the funds are retained, the date until which they are retained. | string (datetime) | **modified_by_user object fields:** | Key | Description | Type | | --- | --- | --- | | id | User ID | integer | | name | User's full name | string | | email | User's email | string | **payment_intent object fields:** | Key | Description | Type | | --- | --- | --- | | id | Payment intent ID | integer | | short_reference | Short reference code | string | | payment_method | Payment method name | string | | custom_payment_id | Custom payment reference | string | | email | Payer's email | string | | item_name | Name of the item | string | | item_description | Description of the item | string | --- # Bob Pay API documentation (legacy) ## Generating a payment URL This documentation describes the legacy method of generating payment URLs by manually constructing query parameters and signatures. For new integrations, we recommend using the modern Payment Intent API, which provides a more streamlined integration experience. To direct your customer to the Bob Pay payment screen using the legacy method, you must construct a URL with query parameters and a security signature. - **Sandbox environment**: `https://sandbox.bobpay.co.za/pay` - **Production environment**: `https://my.bobpay.co.za/pay` ### Required query parameters Include the following query parameters in your payment URL. Replace the example values with your actual data. All parameter values must be URL-encoded. | Parameter | Example Value | Required | Description | | --- | --- | --- | --- | | `recipient_account_code` | `SAN001` | Yes | Your unique Bob Pay account code. | | `custom_payment_id` | `478` | Yes | Your unique reference/identifier for this payment. This will be returned in notifications. | | `email` | `customer@bob.co.za` | Conditional* | Customer's email address. | | `mobile_number` | `+27821234567` | Conditional* | Customer's mobile number in international format (e.g., +27821234567). | | `amount` | `499.99` | Yes | Payment amount in ZAR. Must be greater than zero. | | `item_name` | `Order 1452` | No | Name of the product or service being purchased. | | `item_description` | `Lego Star Wars` | No | Detailed description of the product or service. | | `notify_url` | `https://api-my-store.co.za/payment/bobpay` | Yes | Webhook URL where payment status notifications will be sent. | | `success_url` | `https://my-store.co.za/accounts/payment-confirmation` | Yes | URL to redirect the customer after a successful payment. | | `pending_url` | `https://my-store.co.za/accounts/payment-confirmation` | Yes | URL to redirect the customer when the payment is pending. | | `cancel_url` | `https://my-store.co.za/accounts/payment-cancel` | Yes | URL to redirect the customer if they cancel the payment. | | `signature` | _(generated using MD5 hash, see below)_ | Yes | Security signature to validate data integrity. This must be generated as described below. | | `payment_method` | `instant-eft` _(optional)_ | No | Specific payment method to show the customer. Omit to show all available payment methods. | \* **Note:** Either `email` or `mobile_number` must be provided. ### Generating the signature The `signature` parameter is a security measure to validate data integrity and prevent tampering. You must generate this signature using an MD5 hash of your payment parameters combined with your account passphrase. **Important notes:** - The signature must be calculated from parameters in the **exact order** shown below - The `payment_method` parameter is **NOT** included in signature generation - Your account passphrase is a secret key that can be found in your Bob Pay merchant account settings #### Signature generation steps 1. **Construct the parameter string** by concatenating the following key-value pairs in this exact order, separated by `&`: ```text recipient_account_code custom_payment_id email mobile_number amount item_name item_description notify_url success_url pending_url cancel_url ``` 2. **URL-encode each value** using form encoding, where spaces are encoded as `+` (this matches PHP's `urlencode` and Go's `url.QueryEscape`; with JavaScript's `encodeURIComponent` you must additionally replace `%20` with `+`) 3. **Append your passphrase** to the string: `&passphrase=your_secret_passphrase` 4. **Calculate the MD5 hash** of the complete string to generate the signature #### Example signature generation Let's walk through a complete example: **Step 1:** Construct the parameter string with URL-encoded values: ```text recipient_account_code=SAN001&custom_payment_id=478&email=customer%40bob.co.za&mobile_number=&amount=499.99&item_name=Order+1452&item_description=Lego+Star+Wars¬ify_url=https%3A%2F%2Fapi-sandbox.bobpay.co.za%2Fpayment%2Fbobpay&success_url=https%3A%2F%2Fsandbox.bobpay.co.za%2Faccounts%2Fpayment-confirmation%3Fprovider%3Dbobpay%26id%3D478&pending_url=https%3A%2F%2Fsandbox.bobpay.co.za%2Faccounts%2Fpayment-confirmation%3Fprovider%3Dbobpay%26id%3D478&cancel_url=https%3A%2F%2Fsandbox.bobpay.co.za%2Faccounts%2Fpayment-cancel%3Fprovider%3Dbobpay%26id%3D478 ``` **Step 2:** Append the passphrase (example passphrase: `your_secret_passphrase`): ```text recipient_account_code=SAN001&custom_payment_id=478&email=customer%40bob.co.za&mobile_number=&amount=499.99&item_name=Order+1452&item_description=Lego+Star+Wars¬ify_url=https%3A%2F%2Fapi-sandbox.bobpay.co.za%2Fpayment%2Fbobpay&success_url=https%3A%2F%2Fsandbox.bobpay.co.za%2Faccounts%2Fpayment-confirmation%3Fprovider%3Dbobpay%26id%3D478&pending_url=https%3A%2F%2Fsandbox.bobpay.co.za%2Faccounts%2Fpayment-confirmation%3Fprovider%3Dbobpay%26id%3D478&cancel_url=https%3A%2F%2Fsandbox.bobpay.co.za%2Faccounts%2Fpayment-cancel%3Fprovider%3Dbobpay%26id%3D478&passphrase=your_secret_passphrase ``` **Step 3:** Calculate the MD5 hash of the entire string: Result: `fb07364464def00311c85d306003ef3d` **Step 4:** Append this signature to your payment URL along with all the other parameters before redirecting the customer. #### Complete integration example Below is a complete Node.js example demonstrating how to generate a payment URL with a valid signature: ```javascript const crypto = require("crypto"); // URL-encode a value using form encoding (spaces become "+"), // matching PHP's urlencode and Go's url.QueryEscape function queryEscape(value) { return encodeURIComponent(value) .replace(/%20/g, "+") .replace(/[!'()*]/g, (c) => "%" + c.charCodeAt(0).toString(16).toUpperCase()); } function generatePayURL(bobPayWebsiteURL, kvPairs, passphrase) { const signature = generateSignature(kvPairs, passphrase); const url = `${bobPayWebsiteURL}/pay?${kvPairs .map((kv) => `${kv.key}=${queryEscape(kv.value)}`) .join("&")}&signature=${signature}`; return url; } function generateSignature(kvPairs, passphrase) { const params = kvPairs .map((kv) => `${kv.key}=${queryEscape(kv.value)}`) .join("&"); const stringToHash = `${params}&passphrase=${passphrase}`; return crypto.createHash("md5").update(stringToHash).digest("hex"); } // Example usage const kvPairs = [ { key: "recipient_account_code", value: "SAN001" }, { key: "custom_payment_id", value: "478" }, { key: "email", value: "customer@bob.co.za" }, { key: "mobile_number", value: "" }, { key: "amount", value: "499.99" }, { key: "item_name", value: "Order 1452" }, { key: "item_description", value: "Lego Star Wars" }, { key: "notify_url", value: "https://api-sandbox.bobpay.co.za/payment/bobpay" }, { key: "success_url", value: "https://sandbox.bobpay.co.za/accounts/payment-confirmation?provider=bobpay&id=478", }, { key: "pending_url", value: "https://sandbox.bobpay.co.za/accounts/payment-confirmation?provider=bobpay&id=478", }, { key: "cancel_url", value: "https://sandbox.bobpay.co.za/accounts/payment-cancel?provider=bobpay&id=478", }, ]; const bobPayPassphrase = "your_secret_passphrase"; // Replace with your actual passphrase const bobPayWebsiteURL = "https://sandbox.bobpay.co.za"; const bobPayURL = generatePayURL(bobPayWebsiteURL, kvPairs, bobPayPassphrase); console.log("Payment URL:", bobPayURL); // Redirect your customer to this URL ``` ### Validating your signature To verify that you've generated the signature correctly before going live, you can use the signature validation endpoint: **Endpoint:** `POST /payments/intents/signature` **Base URLs:** - **Sandbox**: `https://api.sandbox.bobpay.co.za/v2/payments/intents/signature` - **Production**: `https://api.bobpay.co.za/v2/payments/intents/signature` **Request body:** Send the payment intent parameters including your generated signature If your signature is valid, the endpoint will return a `200 OK` response. If the signature is invalid, you'll receive a `400 Bad Request` error. ### Payment methods By default, when a customer visits the payment URL, they are presented with all available payment methods enabled for your merchant account. However, you can direct customers to a specific payment method by adding the `payment_method` query parameter to the payment URL. #### Available payment methods | Value | Description | | --- | --- | | `account-balance` | Pay using Bob Pay account balance | | `credit-card` | Credit or debit card payment (Visa, Mastercard, Amex, Diners) | | `apple-pay` | Apple Pay | | `google-pay` | Google Pay | | `instant-eft` | Real-time bank transfer via Instant EFT | | `manual-eft` | Upload proof of payment for manual EFT transfer | | `pay-shap` | PayShap instant payment method | | `capitec-pay` | Capitec Pay instant payment method | | `scan-to-pay` | Scan to Pay (QR code payment) | | `nedbank-direct-eft` | Nedbank Direct EFT instant payment method | | `absa-pay` | Absa Pay instant payment method | ## Handling notifications When a payment is completed, Bob Pay sends a POST notification to the `notify_url` you specified. This webhook notification contains the complete payment details and requires proper security validation. ### Security & validation requirements To ensure the webhook is legitimate and the payment is valid, you **must** perform the following security checks: #### 1. Verify source IP address All webhook requests originate from Bob Pay's static IP addresses. Verify that the request comes from one of these IPs: - **Sandbox environment:** `13.245.58.93` - **Production environment:** `13.246.100.25` #### 2. Validate the signature The webhook payload includes a `signature` field. Verify this signature using the same MD5 hashing process described in the "Generating the Signature" section above. This confirms the data hasn't been tampered with. **Note:** Use your own `recipient_account_code` value when recalculating the signature — the payload's top-level `recipient_account_code` field is always empty (your account code is also available in `recipient_account.account_code`). #### 3. Verify the amount Ensure the `paid_amount` in the webhook matches the expected amount for your order. Note that some payment methods may allow non-exact payments if enabled for your account. #### 4. Validate with Bob Pay After verifying the signature and amount, confirm the payment's validity with Bob Pay by calling the validation endpoint. **Endpoint:** `POST /payments/intents/validate` **Base URLs:** - **Sandbox**: `https://api.sandbox.bobpay.co.za/v2/payments/intents/validate` - **Production**: `https://api.bobpay.co.za/v2/payments/intents/validate` **Request body:** Send the complete webhook payload you received. ### Responding to webhooks **Critical:** You must return a `200 OK` HTTP status code to acknowledge receipt of the webhook. If Bob Pay doesn't receive a 200 OK response, it will retry sending the webhook with increasing delays, up to 17 retries over approximately 4 days. **Best practices:** - Make your webhook endpoint idempotent (able to handle duplicate notifications safely) - Use the `uuid` or `custom_payment_id` to prevent processing the same payment multiple times - Store webhook receipts in your database to track which notifications you've already processed - Return `200 OK` as quickly as possible; perform time-consuming operations asynchronously