# Authentication Source: https://docs.sudo.africa/docs/authentication Sudo's API uses OAuth 2.0 Bearer Token to authenticate requests. All API calls must include a bearer token. ```curl curl theme={null} GET /cards HTTP/1.1 Host: api.sandbox.sudo.africa Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cC... ``` An invalid, missing, or expired token will result in `HTTP 401 Unauthorized` responses. # Authorizations Source: https://docs.sudo.africa/docs/authorizations When a card is used either at an ATM, POS or Online, an authorization request is made which is approved or declined based on the following steps. 1. User attempts to make a transaction at an ATM, POS or Online. 2. An Authorization object is created on Sudo. 3. Sudo checks to ensure the current available balance is sufficient for the transaction. 4. Sudo proceeds to check the card and cardholder status. We expect both to be active. 5. Sudo checks if spending controls should automatically decline the authorization. 6. Sudo then sends an `authorization.request` event which you are expected to either approve or decline. 7. If we do not receive a response from you within **4** seconds, the `Authorization` is automatically approved or declined based on your timeout settings as configured in the card funding source. ## Approved Authorizations Once an authorization request is made, the status on the authorization is set to `pending`, and the `authorization.request` webhook event is sent. On approval, the `amount` is deducted from your default available balance. A transaction is then created and the `status` of the authorization is set to `closed`. # Android Native SDK Source: https://docs.sudo.africa/docs/cloud-card/android-native-sdk Integrate the Sudo Cloud Card Android Native SDK for provisioning, management, NFC, QR, and payment operations. ## Package * Artifact: `africa.sudo:cloudcard` * Version: **0.2.0** * Entry point: `CloudCardAndroid` ## Overview The Cloud Card Android SDK provides APIs for: * Card provisioning and registration * Card lifecycle management (freeze, unfreeze, delete, wipe) * Multi-card support with active and default card selection * QR-based transactions * Token usage summaries and key replenishment * NFC status checks, chip-position indicators, and payment-app settings * Locally cached transaction history ## Installation Add the SDK repository and dependency to your Android app module setup. ```groovy theme={null} repositories { google() mavenCentral() maven { url = uri("https://sdk.sudo.africa/repository/maven-releases/") credentials { username = "USERNAME" password = "PASSWORD" } } } dependencies { implementation("africa.sudo:cloudcard:0.2.0") } ``` Keep your repository credentials in `gradle.properties` or environment variables rather than committing them to source control. ## Android Manifest Add the following service inside the `` tag of your `AndroidManifest.xml`. ```xml theme={null} ``` ## Initialization `CloudCardAndroid` is a singleton. Call `getInstance()` once during app startup with a `Context`; later calls can omit it. ```kotlin theme={null} import com.sudo.cloud_card.* val cloudCard = CloudCardAndroid.getInstance( context = applicationContext, isSandBox = true, supportMultipleCards = false, url = null ) ``` | Parameter | Type | Default | Description | | ---------------------- | ---------- | ------- | --------------------------------------------------------- | | `context` | `Context?` | `null` | Required on the first call. Pass the application context. | | `isSandBox` | `Boolean` | `true` | Selects the sandbox or production environment. | | `supportMultipleCards` | `Boolean` | `false` | Allows more than one card in the wallet. | | `url` | `String?` | `null` | Optional base URL override. | The first call to `getInstance()` must provide a `Context`. Calling it without one before the SDK is initialized throws `IllegalStateException`. `getInstance()` calls `init()` internally, so you do not need to call `init()` yourself: ```kotlin theme={null} fun init( context: Context, isSandBox: Boolean, useNotifications: Boolean, supportMultipleCards: Boolean = false, url: String? ) ``` ## Device and NFC Checks ### `isNfcEnabled()` ```kotlin theme={null} fun isNfcEnabled(): Boolean? ``` Checks NFC availability and state. This method is nullable and has three outcomes: * `true`: NFC is available and enabled * `false`: NFC is available but not enabled * `null`: NFC is not available on the device ### `isDeviceSupported()` ```kotlin theme={null} fun isDeviceSupported(): Boolean ``` Returns `true` if the device meets the required security and compatibility checks. ### `isDefaultPaymentApp()` ```kotlin theme={null} fun isDefaultPaymentApp(): Boolean ``` Returns `true` if your application is currently set as the default contactless payment app. ### `launchDefaultPaymentAppSettings(activity: Activity)` ```kotlin theme={null} fun launchDefaultPaymentAppSettings(activity: Activity): Boolean ``` Opens the device settings page where the user can set your app as the default payment app. Returns `true` if the settings page was launched. ```kotlin theme={null} if (cloudCard.isNfcEnabled() == true && !cloudCard.isDefaultPaymentApp()) { cloudCard.launchDefaultPaymentAppSettings(this) } ``` ## NFC Chip Position These helpers show users where to tap on the device for contactless payments. ### `showNfcChipPosition(activity, duration, colorHex)` ```kotlin theme={null} fun showNfcChipPosition( activity: Activity, duration: Long? = 3000L, colorHex: String? = null ): Boolean ``` Displays an overlay indicator over the NFC chip location. * `activity`: activity used to display the overlay * `duration`: display time in milliseconds. Pass `null` to show it indefinitely * `colorHex`: optional indicator color, for example `"#FF5722"` Returns `true` if the indicator was displayed. ### `hideNfcChipPosition()` ```kotlin theme={null} fun hideNfcChipPosition(): Boolean ``` Dismisses the indicator. Returns `false` if no indicator was showing. ### `getNfcChipPosition()` ```kotlin theme={null} fun getNfcChipPosition(): NfcChipPosition? ``` Returns the chip coordinates and confidence level, or `null` if the position cannot be determined. ```kotlin theme={null} cloudCard.showNfcChipPosition(activity = this, duration = 5000L, colorHex = "#AF5298") val position = cloudCard.getNfcChipPosition() Log.d("CloudCard", "${position?.x} ${position?.y} ${position?.confidence}") cloudCard.hideNfcChipPosition() ``` ## Card Registration ### `registerCard(data: RegistrationData)` ```kotlin theme={null} suspend fun registerCard(data: RegistrationData): CCResult ``` Registers and provisions a card using institution and device information. This is a suspend function, so call it from a coroutine. ```kotlin theme={null} lifecycleScope.launch { val result = cloudCard.registerCard( RegistrationData( walletId = walletId, paymentAppInstanceId = paymentAppInstanceId, accountId = accountId, jwtToken = jwtToken ) ) if (result.status == Status.SUCCESS) { // card provisioned } } ``` ## Card Management ### `getCards()` ```kotlin theme={null} fun getCards(): List ``` Returns all cards available in the wallet. ### `setActiveCard(id: String)` ```kotlin theme={null} fun setActiveCard(id: String): Boolean ``` Sets the card matching the given `tokenUniqueRef` as the active card used for the next payment. ### `setDefaultCard(id: String)` ```kotlin theme={null} fun setDefaultCard(id: String): Boolean ``` Sets the card matching the given `tokenUniqueRef` as the default card used for payments. `setActiveCard` and `setDefaultCard` are relevant when the SDK is initialized with `supportMultipleCards = true`. ### `freezeUnfreezeCard(period: Duration?, cardId: String)` ```kotlin theme={null} fun freezeUnfreezeCard(period: Duration?, cardId: String): CCResult ``` Toggles the card between active and frozen states. * `period`: optional duration for which the card should stay frozen * `cardId`: required card identifier Returns a `CCResult` whose `data` field is a `Boolean` representing the latest card state. ### `deleteCard(cardId: String)` ```kotlin theme={null} fun deleteCard(cardId: String): CCResult ``` Permanently deletes a single card from the wallet. ### `emptyWallet()` ```kotlin theme={null} fun emptyWallet(): CCResult ``` Deletes all card and transaction data in the wallet. ### `wipeWallet()` ```kotlin theme={null} fun wipeWallet(): CCResult ``` Wipes all wallet data and preferences stored by the SDK, including card information, tokens, and wallet configuration. `emptyWallet()` and `wipeWallet()` are destructive and cannot be undone. Cards must be provisioned again afterwards. ## Tokens and Security ### `manualKeyReplenishment()` ```kotlin theme={null} suspend fun manualKeyReplenishment(): CCResult ``` Refreshes the secure keys used for offline transactions on the current card. ### `tokenSummary()` ```kotlin theme={null} fun tokenSummary(): TokensUsageSummary ``` Returns token usage statistics across the card lifecycle. ### `getTokenThreshold()` ```kotlin theme={null} fun getTokenThreshold(): Int ``` Returns the token count at which replenishment should be triggered. Compare it against the values in `tokenSummary()` to decide when to call `manualKeyReplenishment()`. ### `setRequireAuth(auth: Boolean)` ```kotlin theme={null} fun setRequireAuth(auth: Boolean) ``` Set to `true` to enforce foreground card access and a biometric check before transactions. ## QR and Transactions ### `getEmvQr(...)` ```kotlin theme={null} suspend fun getEmvQr( cardId: String, amount: String? = null, foregroundColorHex: String? = null, backgroundColorHex: String? = null, margin: Int? = 4 ): CCResult ``` Generates an EMV QR code for a customer-presented QR payment. * `cardId`: required card identifier * `amount`: transaction amount in the lowest denomination, as a 12-character zero-padded string. For example, NGN 1 is `"000000000100"`. Leave it empty or `null` to generate a dynamic-amount QR * `foregroundColorHex`: QR code color. Defaults to black * `backgroundColorHex`: QR background color. Defaults to transparent * `margin`: margin between the QR code and its background. Defaults to `4` Returns a `CCResult` whose `data` field contains a `Bitmap`. ```kotlin theme={null} lifecycleScope.launch { val result = cloudCard.getEmvQr( cardId = cardId, amount = "000000000200", foregroundColorHex = "#000000", backgroundColorHex = "#FFFFFF" ) if (result.status == Status.SUCCESS) { qrImageView.setImageBitmap(result.data as Bitmap) } } ``` ### `getSavedTransactions()` ```kotlin theme={null} fun getSavedTransactions(): List ``` Returns recent completed transactions cached on the device. ## Objects ### `RegistrationData` Required fields: * `walletId`: institution ID * `paymentAppInstanceId`: unique device ID * `accountId`: card unique identifier * `jwtToken`: auth token from your server Optional fields: * `secret`: secret passed to the user * `cardNumber`: card number for an already-issued card * `expiryDate`: expiry for an already-issued card * `cardHolderName`: cardholder name for an already-issued card ### `CCResult` Fields: * `status`: request status. Possible values are `SUCCESS`, `FAILED`, `PENDING` * `message`: accompanying message * `data`: present on successful responses ### `CardData` Fields: * `isActive`: current card state. `false` means the card is frozen * `id`: unique card ID * `maskedPan`: masked PAN * `cardHolder`: cardholder name * `exp`: card expiry data ### `NfcChipPosition` Fields: * `x`: horizontal coordinate of the NFC chip * `y`: vertical coordinate of the NFC chip * `confidence`: confidence level of the detected position ### `SavedTransaction` Fields: * `amount`: transaction amount * `currency`: transaction currency * `timestamp`: time the transaction completed * `type`: transaction type ## Customization ### APDU Service Banner To override the default Host APDU Service banner used by the library, add a drawable resource with the same name in your application: * Create `cloud_card_banner` in your app's `res/drawable` directory. # Flutter SDK Source: https://docs.sudo.africa/docs/cloud-card/flutter-sdk Sudo Cloud Card Flutter SDK. ## Package * SDK: `cloudcard_flutter` * Version: **0.1.1** ([pub.dev/packages/cloudcard\_flutter](https://pub.dev/packages/cloudcard_flutter)) For terminal transaction processing, Flutter has a companion package: flutter\_tappa. ## Installation Add the dependency: ```yaml theme={null} # pubspec.yaml dependencies: cloudcard_flutter: ^0.1.1 ``` Then run: ```bash theme={null} flutter pub get ``` ## Android Repository Setup For Gradle \< 7 (`android/build.gradle`): ```groovy theme={null} allprojects { repositories { google() mavenCentral() maven { url = uri("https://sdk.sudo.africa/repository/maven-releases/") credentials { username = project.findProperty("maven.repo.username") ?: "" password = project.findProperty("maven.repo.password") ?: "" } } } } ``` For Gradle 7+ (`android/settings.gradle`): ```groovy theme={null} dependencyResolutionManagement { repositories { google() mavenCentral() maven { url = uri("https://sdk.sudo.africa/repository/maven-releases/") credentials { username = providers.gradleProperty("maven.repo.username").orNull ?: System.getenv("NEXUS_USER") password = providers.gradleProperty("maven.repo.password").orNull ?: System.getenv("NEXUS_PASS") } } } } ``` Set credentials in `gradle.properties`: ```properties theme={null} maven.repo.username=your_sudo_username maven.repo.password=your_sudo_password ``` Or set environment variables: ```bash theme={null} export NEXUS_USER=your_sudo_username export NEXUS_PASS=your_sudo_password ``` ## Android Manifest Configuration Register the HCE service and receiver inside `` in `android/app/src/main/AndroidManifest.xml`. ```xml theme={null} ``` ## iOS Setup Create `.env` in your project root: ```bash theme={null} SUDO_USER=your_sudo_username SUDO_PASS=your_sudo_password ``` Run: ```bash theme={null} export $(grep -v '^#' .env | xargs) TMPHOME=$(mktemp -d) trap 'rm -rf "$TMPHOME"' EXIT export HOME="$TMPHOME" cat > "$HOME/.netrc" < initializeCloudCard() async { await cloudCard.init( isSandBox: true, onCardScanned: (CloudCardEvent event) { print('scan started: ${event.eventType} ${event.message}'); }, onScanComplete: (CloudCardEvent event) { print('scan done: ${event.isSuccess} amount=${event.amount}'); }, ); } ``` Environment: * `isSandBox: true` for sandbox * `isSandBox: false` for production ## Health Checks ```dart theme={null} final isSupported = await cloudCard.isDeviceSupported(); final isNfcEnabled = await cloudCard.isNfcEnabled(); final isDefaultApp = await cloudCard.isDefaultPaymentApp(); if (isSupported && isNfcEnabled == true && isDefaultApp == false) { await cloudCard.launchDefaultPaymentAppSettings(); } ``` ## Optional Security Behavior ```dart theme={null} await cloudCard.setRequireAuth(true); ``` Use `setRequireAuth(true)` if you want foreground access and biometric checks during payment access. ## Register Card You register cards with `RegistrationData`. Required fields: * `walletId` * `paymentAppInstanceId` * `accountId` * `jwtToken` Optional fields: * `secret` * `cardNumber` * `expiryDate` * `cardHolderName` ```dart theme={null} final cloudCard = CloudCardFlutter(); final registrationData = RegistrationData( walletId: 'institution-id', paymentAppInstanceId: 'device-instance-id', accountId: 'card-or-account-id', jwtToken: 'onboarding-jwt-token', secret: 'optional-secret', cardNumber: '4111111111111111', expiryDate: '12/25', cardHolderName: 'John Doe', ); final result = await cloudCard.registerCard(registrationData); if (result.status == Status.SUCCESS) { print('Card registered'); } else { print('Registration failed: ${result.message}'); } ``` ## Digitalization Fetch Pattern If you already have your own card-management system, you can build and pass your own `RegistrationData` directly to `registerCard` using the required fields `walletId`, `paymentAppInstanceId`, `accountId`, and `jwtToken`. If you do not manage that payload yourself, fetch the onboarding or digitalization payload from your backend first, then pass it to `registerCard`. For endpoint details, see [Card Digitalization Reference](/reference/card-digitalization). ```dart theme={null} final digitalizeUrl = Uri.parse( 'https://api.sandbox.sudo.cards/cards/digitalize/{id}', ); final res = await http.get( digitalizeUrl, headers: { 'Accept': 'text/plain', 'Content-Type': 'application/json', 'platform': 'android', 'Authorization': accessToken, }, ); ``` ## Card Management ### Get Cards ```dart theme={null} final result = await CloudCardFlutter().getCards(); if (result.status == Status.SUCCESS && result.data is List) { final cards = result.data as List; for (final card in cards) { print('${card.id} ${card.maskedPan} active=${card.isActive}'); } } ``` ### Freeze or Unfreeze ```dart theme={null} await CloudCardFlutter().freezeUnfreezeCard( cardId: cardId, isFreeze: true, ); ``` Set `isFreeze: false` to unfreeze. ### Delete Card ```dart theme={null} await CloudCardFlutter().deleteCard(cardId); ``` ### Wipe Wallet ```dart theme={null} await CloudCardFlutter().wipeWallet(); ``` Use this only after explicit user confirmation. ### Manual Key Replenishment ```dart theme={null} final result = await CloudCardFlutter().manualKeyReplenishment(); ``` ### Token Usage Summary ```dart theme={null} final result = await CloudCardFlutter().tokenSummary(); if (result.status == Status.SUCCESS && result.data != null) { final summary = TokensUsageSummary.fromMap(result.data); print('total=${summary.totalTokens} balance=${summary.tokensBalance}'); } ``` ## QR and Transactions ### Generate EMV QR ```dart theme={null} final result = await CloudCardFlutter().getEmvQr( cardId: cardId, amount: '000000000200', foregroundColorHex: '#000000', backgroundColorHex: '#FFFFFF', ); if (result.status == Status.SUCCESS && result.data != null) { showDialog( context: context, builder: (_) => AlertDialog(content: Image.memory(result.data)), ); } ``` `amount` is in the smallest unit and typically zero-padded. ### Read Saved Transactions `getSavedTransactions()` returns recent cached transactions, up to 5. ```dart theme={null} final result = await CloudCardFlutter().getSavedTransactions(); if (result.status == Status.SUCCESS && result.data is List) { final txs = result.data as List; for (final tx in txs) { print('${tx.amount} ${tx.currency} ${tx.timestamp} ${tx.type}'); } } ``` ## NFC and Settings ```dart theme={null} final cloudCard = CloudCardFlutter(); final isNfcEnabled = await cloudCard.isNfcEnabled(); final isDefaultPaymentApp = await cloudCard.isDefaultPaymentApp(); if (isNfcEnabled == true && isDefaultPaymentApp == false) { await cloudCard.launchDefaultPaymentAppSettings(); } ``` ### NFC Chip Indicator Helpers ```dart theme={null} await cloudCard.showNfcChipIndicator( duration: const Duration(seconds: 5), colorHex: '#AF5298', ); final position = await cloudCard.getNfcChipPosition(); print('${position?.x} ${position?.y} ${position?.confidence}'); await cloudCard.hideNfcChipIndicator(); ``` ## Requirements * Flutter 2.5.0+ * Android API 21+ * iOS 13.0+ NFC-enabled device is required for NFC payments, but not for QR. ## Troubleshooting ### Initialization Fails * Confirm SDK repository credentials are set correctly on Android. * Confirm the iOS `.netrc` auth step was completed before `pod install`. * Check that you called `init()` before card operations. ### NFC Not Working * Confirm the device supports NFC. * Confirm NFC is enabled in system settings. * Confirm your app is set as default payment app when required. * Verify the HCE service and receiver entries in `AndroidManifest.xml`. * Confirm the receiver class is `africa.sudo.cloudcard_flutter.HceEventReceiver`. ### Card Registration Fails * Verify `walletId`, `paymentAppInstanceId`, `accountId`, and `jwtToken`. * Ensure the onboarding token is still valid. * Check that your backend is issuing tokens for the correct environment. ### QR Generation Fails * Use a valid `cardId`. * Ensure `amount` is formatted correctly. ### Empty Transaction List * `getSavedTransactions()` uses local cache and returns recent transactions only. * Complete at least one transaction flow first, then reload. ## Support Contact [support@sudo.africa](mailto:support@sudo.africa). # Overview Source: https://docs.sudo.africa/docs/cloud-card/introduction Overview of the Sudo Cloud Card integration flow for Flutter, React Native, Android Native, and iOS Native SDKs. Sudo Cloud Card supports Flutter, React Native, Android Native, and iOS Native SDKs for digitization and wallet operations. Supported SDKs: * Flutter: `cloudcard_flutter` * React Native: `react-native-cloudcard` * Native SDK: Android * Native SDK: iOS It supports: * Card provisioning and registration * Card lifecycle actions (freeze, unfreeze, delete, wipe) * EMV QR generation for customer-presented payments * NFC status and default payment app checks * Local transaction history and token usage summaries The latest SDK versions are: * Flutter: **0.1.1** ([pub.dev/packages/cloudcard\_flutter](https://pub.dev/packages/cloudcard_flutter)) ([guide](/docs/cloud-card/flutter-sdk)) * React Native: **0.1.1** ([npmjs.com/package/react-native-cloudcard](https://www.npmjs.com/package/react-native-cloudcard)) ([guide](/docs/cloud-card/react-native-sdk)) * Native SDK Android: **0.2.0** (`africa.sudo:cloudcard`) ([guide](/docs/cloud-card/android-native-sdk)) * Native SDK iOS: [iOS SDK guide](/docs/cloud-card/ios-native-sdk) ## Integration Summary No matter which SDK you choose, the setup flow is broadly the same: 1. Install the SDK package or native dependency for your platform. 2. Configure Android repository access, manifest entries, and any required iOS dependency setup. 3. Initialize the SDK once during app startup and register scan callbacks. 4. Run device checks such as NFC availability, supported-device validation, and default payment app status. 5. If you manage your own card-management system, you can pass your own `RegistrationData` with the required fields `walletId`, `paymentAppInstanceId`, `accountId`, and `jwtToken`. If you do not manage that payload yourself, fetch the onboarding or digitalization payload from your backend first, then pass it to registerCard. [Card Digitalization Reference](/reference/card-digitalization). 6. Use the registered card for lifecycle actions such as freeze or unfreeze, key replenishment, wallet wipe, and token usage monitoring. 7. Generate EMV QR codes, inspect saved transactions, and guide users to NFC or payment-app settings where needed. The platform-specific guides below contain the exact implementation details and code samples for each SDK. ## Guides 1. [Flutter SDK](/docs/cloud-card/flutter-sdk) 2. [React Native SDK](/docs/cloud-card/react-native-sdk) 3. [Android Native SDK](/docs/cloud-card/android-native-sdk) 4. [iOS Native SDK](/docs/cloud-card/ios-native-sdk) # iOS Native SDK Source: https://docs.sudo.africa/docs/cloud-card/ios-native-sdk Integrate the Sudo Cloud Card iOS Native SDK for provisioning, token management, QR generation, and local transaction access. ## Overview The Cloud Card iOS SDK provides APIs for: * Card provisioning and registration * Retrieving locally stored cards * Generating EMV QR codes * Reading token inventory and usage summaries * Accessing locally cached transactions The native entry point exposed by the SDK is `CloudCardWrapper`. ## Installation `CloudCard` is distributed as an `XCFramework` through Sudo Africa's protected artifact repository. ### Requirements * iOS 13.0+ * Xcode 15+ * CocoaPods * Physical iPhone recommended for payment testing ### SDK Repository Authentication You need repository credentials from Sudo Africa before installation. ```bash theme={null} export SUDO_USER="your_sdk_username" export SUDO_PASSWORD="your_sdk_password" ``` Or load them from `.env`: ```bash theme={null} export $(grep -v '^#' .env | xargs) ``` Use a temporary `.netrc` file when installing: ```bash theme={null} TMPHOME=$(mktemp -d) trap 'rm -rf "$TMPHOME"' EXIT export HOME="$TMPHOME" cat > "$HOME/.netrc" < Do not commit `.env`, `.netrc`, or SDK credentials to source control. ## Environments The SDK supports two environments: * `true` for sandbox * `false` for production Your SDK environment must match the backend environment issuing the provisioning payload. ## Initialization Initialize the SDK once during app startup before calling any other Cloud Card API. ```swift theme={null} import CloudCard CloudCardWrapper.initializeSDK(true) ``` Use `true` for sandbox and `false` for production. ## Register a Card Provisioning data must come from your backend after the user is authenticated. The client app should not generate provisioning credentials locally. Typical payload values include: * `walletId` * `accountId` * `paymentAppInstanceId` * `secret` * `jwtToken` Example: ```swift theme={null} import CloudCard let data = WalletRequestIOS() data.accountId = accountId data.tokenUniqueReference = accountId data.paymentAppInstanceId = paymentAppInstanceId data.secret = secret data.keyNumber = 0 data.walletId = walletId data.jwtToken = jwt let result = await CloudCardWrapper.registerCard(data) ``` Successful registration stores the provisioned card locally on the device. ## Retrieve Cards ```swift theme={null} if let fetched = CloudCardWrapper.getCards() as? [Any] { // Map results to your UI model } ``` Filter active cards before displaying them to users. ## Token Summary Use token summary to monitor the current token balance and refresh state after provisioning or payment activity. ```swift theme={null} if let summaryResult = CloudCardWrapper.getTokenSummary() as? [String: Any] { let totalTokens = summaryResult["totalTokens"] as? Int let tokensBalance = summaryResult["tokensBalance"] as? Int } ``` ## Retrieve Saved Transactions ```swift theme={null} let raw = CloudCardWrapper.getSavedTransactions() ``` Saved transaction records typically include: * ATC * Amount * Timestamp ## Generate EMV QR ```swift theme={null} if let result = CloudCardWrapper.getEmvQr( amount, foregroundColorHex: "#000000", backgroundColorHex: "#FFFFFF" ), let status = result["status"] as? String, status == "SUCCESS", let qrImageResult = result["data"] as? UIImage { // Render QR image } ``` The QR API supports dynamic amounts and custom foreground or background colors. ## Common Issues ### Authentication Failure During Installation Check: * `SUDO_USER` is set * `SUDO_PASSWORD` is set * Credentials are valid * `.netrc` is configured correctly ### Works on Device but Fails on Simulator If the XCFramework only includes `ios-arm64`, simulator builds will fail. Use a physical iPhone or request a simulator-compatible XCFramework. ### Registration Succeeds but No Cards Are Returned Verify: * SDK environment matches backend environment * `walletId` and `accountId` are correct * registration returned success * stored cards are active ### QR Generation Fails Check: * there is an active card on the device * token inventory is not depleted * the amount format is valid * SDK environment matches the provisioning environment ## Best Practices * Initialize the SDK once during app startup * Use a stable device identifier for `paymentAppInstanceId` * Fetch provisioning data from a secure backend endpoint * Avoid logging secrets or JWTs in production * Refresh cards and token summary after provisioning or payment events # React Native SDK Source: https://docs.sudo.africa/docs/cloud-card/react-native-sdk Sudo Cloud Card React Native SDK. ## Package * SDK: `react-native-cloudcard` * Version: **0.1.1** ([npmjs.com/package/react-native-cloudcard](https://www.npmjs.com/package/react-native-cloudcard)) ## Installation ```bash theme={null} npm install react-native-cloudcard # or yarn add react-native-cloudcard ``` ## Android Repository Setup For Gradle \< 7 (`android/build.gradle`): ```groovy theme={null} allprojects { repositories { google() mavenCentral() maven { url = uri("https://sdk.sudo.africa/repository/maven-releases/") credentials { username = project.findProperty("maven.repo.username") ?: "" password = project.findProperty("maven.repo.password") ?: "" } } } } ``` For Gradle 7+ (`android/settings.gradle`): ```groovy theme={null} dependencyResolutionManagement { repositories { google() mavenCentral() maven { url = uri("https://sdk.sudo.africa/repository/maven-releases/") credentials { username = providers.gradleProperty("maven.repo.username").orNull ?: System.getenv("NEXUS_USER") password = providers.gradleProperty("maven.repo.password").orNull ?: System.getenv("NEXUS_PASS") } } } } ``` Set credentials in `gradle.properties`: ```properties theme={null} maven.repo.username=your_sudo_username maven.repo.password=your_sudo_password ``` Or set environment variables: ```bash theme={null} export NEXUS_USER=your_sudo_username export NEXUS_PASS=your_sudo_password ``` ## Android Manifest Configuration Register the HCE service and receiver inside `` in `android/app/src/main/AndroidManifest.xml`. ```xml theme={null} ``` ## iOS Setup Create `.env` in your project root: ```bash theme={null} SUDO_USER=your_sudo_username SUDO_PASS=your_sudo_password ``` Run: ```bash theme={null} export $(grep -v '^#' .env | xargs) TMPHOME=$(mktemp -d) trap 'rm -rf "$TMPHOME"' EXIT export HOME="$TMPHOME" cat > "$HOME/.netrc" < { console.log('scan started', event?.eventType ?? event?.event_type, event?.message); }, onScanComplete: (event) => { console.log('scan done', event?.isSuccess ?? event?.is_success, event?.amount); }, }); } ``` Environment: * `isSandBox: true` for sandbox * `isSandBox: false` for production ## Health Checks ```ts theme={null} const isSupported = await CloudCard.isDeviceSupported(); const isNfcEnabled = await CloudCard.isNfcEnabled(); const isDefaultApp = await CloudCard.isDefaultPaymentApp(); if (isSupported && isNfcEnabled === true && isDefaultApp === false) { await CloudCard.launchDefaultPaymentAppSettings(); } ``` ## Optional Security Behavior ```ts theme={null} await CloudCard.setRequireAuth(true); ``` ## Register Card You register cards with `RegistrationData`. Required fields: * `walletId` * `paymentAppInstanceId` * `accountId` * `jwtToken` Optional fields: * `secret` * `cardNumber` * `expiryDate` * `cardHolderName` ```ts theme={null} import CloudCard from 'react-native-cloudcard'; const result = await CloudCard.registerCard({ walletId: 'institution-id', paymentAppInstanceId: 'device-instance-id', accountId: 'card-or-account-id', jwtToken: 'onboarding-jwt-token', secret: 'optional-secret', cardNumber: '4111111111111111', expiryDate: '12/25', cardHolderName: 'John Doe', }); if (result.status === 'SUCCESS') { console.log('Card registered'); } else { console.log(`Registration failed: ${result.message}`); } ``` ## Digitalization Fetch Pattern If you already have your own card-management system, you can build and pass your own `RegistrationData` directly to `registerCard` using the required fields `walletId`, `paymentAppInstanceId`, `accountId`, and `jwtToken`. If you do not manage that payload yourself, fetch the onboarding or digitalization payload from your backend first, then pass it to `registerCard`. For endpoint details, see [Card Digitalization Reference](/reference/card-digitalization). ```ts theme={null} const res = await fetch( `https://api.sandbox.sudo.cards/cards/digitalize/{id}`, { method: 'GET', headers: { Accept: 'text/plain', 'Content-Type': 'application/json', platform: 'android', Authorization: accessToken, }, } ); const jwtToken = await res.text(); ``` ## Card Management ### Get Cards ```ts theme={null} import CloudCard from 'react-native-cloudcard'; const result = await CloudCard.getCards(); if (result.status === 'SUCCESS' && Array.isArray(result.data)) { result.data.forEach((card) => { console.log(card.id, card.maskedPan, `active=${card.isActive}`); }); } ``` ### Freeze or Unfreeze ```ts theme={null} await CloudCard.freezeUnfreezeCard({ cardId, isFreeze: true, periodInDays: 7, }); ``` Set `isFreeze: false` to unfreeze. ### Delete Card ```ts theme={null} await CloudCard.deleteCard(cardId); ``` ### Wipe Wallet ```ts theme={null} await CloudCard.wipeWallet(); ``` Use this only after explicit user confirmation. ### Manual Key Replenishment ```ts theme={null} const result = await CloudCard.manualKeyReplenishment(); ``` ### Token Usage Summary ```ts theme={null} const result = await CloudCard.tokenSummary(); if (result.status === 'SUCCESS' && result.data) { console.log( `total=${result.data.totalTokens} balance=${result.data.tokensBalance}` ); } ``` ## QR and Transactions ### Generate EMV QR ```ts theme={null} import CloudCard from 'react-native-cloudcard'; const result = await CloudCard.getEmvQr({ cardId, amount: '000000000200', foregroundColorHex: '#000000', backgroundColorHex: '#FFFFFF', }); if (result.status === 'SUCCESS' && result.data) { const uri = `data:image/png;base64,${result.data}`; console.log('QR ready', uri); } ``` `amount` is in the smallest unit and typically zero-padded. ### Read Saved Transactions `getSavedTransactions()` returns recent cached transactions, up to 5. ```ts theme={null} const result = await CloudCard.getSavedTransactions(); if (result.status === 'SUCCESS' && Array.isArray(result.data)) { result.data.forEach((tx) => { console.log(tx.amount, tx.currency, tx.timestamp, tx.type); }); } ``` ## NFC and Settings ```ts theme={null} import CloudCard from 'react-native-cloudcard'; const isNfcEnabled = await CloudCard.isNfcEnabled(); const isDefaultPaymentApp = await CloudCard.isDefaultPaymentApp(); if (isNfcEnabled === true && isDefaultPaymentApp === false) { await CloudCard.launchDefaultPaymentAppSettings(); } ``` ### NFC Chip Indicator Helpers ```ts theme={null} await CloudCard.showNfcChipPosition({ duration: 5000, colorHex: '#AF5298', }); const position = await CloudCard.getNfcChipPosition(); console.log(position?.x, position?.y, position?.radius, position?.hint); await CloudCard.hideNfcChipPosition(); ``` ## Requirements * React Native 0.68+ * Android API 24+ * iOS 13.0+ NFC-enabled device is required for NFC payments, but not for QR. ## Troubleshooting ### Initialization Fails * Confirm SDK repository credentials are set correctly on Android. * Confirm the iOS `.netrc` auth step was completed before `pod install`. * Check that you called `init()` before card operations. * Rebuild the app after native dependency changes. * Run `cd ios && pod install` for iOS before rebuilding. ### NFC Not Working * Confirm the device supports NFC. * Confirm NFC is enabled in system settings. * Confirm your app is set as default payment app when required. * Verify the HCE service and receiver entries in `AndroidManifest.xml`. * Confirm the receiver class is `africa.sudo.cloudcard.reactnative.HceEventReceiver`. ### Card Registration Fails * Verify `walletId`, `paymentAppInstanceId`, `accountId`, and `jwtToken`. * Ensure the onboarding token is still valid. * Check that your backend is issuing tokens for the correct environment. ### QR Generation Fails * Use a valid `cardId`. * Ensure `amount` is formatted correctly. ### Empty Transaction List * `getSavedTransactions()` uses local cache and returns recent transactions only. * Complete at least one transaction flow first, then reload. ## Support Contact [support@sudo.africa](mailto:support@sudo.africa). # Create API Key Source: https://docs.sudo.africa/docs/create-api-key Creating an API Key can be easily done on your dashboard by following these steps. # How to Create API Key 1. Log in to the Sudo Dashboard and click on **Developers**. 2. Click on **API Keys** in the **Developers** navigation menu seen horizontally on your screen. Click on the **Create API Key** button, located on the right-hand side of your screen. 2880 3. Then fill in the required details for your key, such as **Name**, **Validity Period**, and **IP Address** (Optional) as indicated in Steps 4, 5, and 6 respectively. 4. Once you're done, click on the **Create API Key** button on the modal, as shown in Step 7. 5. Copy the API Key shown on your screen as it will be shown only once and can not be retrieved afterward. 2880 If an IP address is provided, only requests originating from that IP will be allowed. If no IP is provided, all requests will be denied. ## IP Whitelist The IP Whitelist system is a security layer used to restrict access to protected API Keys by allowing requests only from explicitly approved IP addresses or IP ranges. ### Supported IP Formats Sudo supports the following IP formats for flexibility and security: | Format Type | Description | Example | | ---------------- | -------------------------------------------- | -------------------------------------- | | Exact IPv4 | Matches a single IPv4 address | 192.168.1.10 | | Exact IPv6 | Matches a single IPv6 address | 2c0f:eb58:612:3500:e839:c029:487f:12a7 | | IPv6-mapped IPv4 | Automatically normalized | ::ffff:192.168.1.10 → 192.168.1.10 | | CIDR Notation | Matches a subnet range | 192.168.0.0/16 or 2607:f8b0::/32 | | Regex Pattern | Dynamic IP matching with regular expressions | \`/^10.0.(1 | | Wildcard | Automatically allows all IPs | \*, 0.0.0.0, "", \[\*] | Think your API Key is compromised or stolen, you can delete an API Key by clicking on the delete button beside each key as shown on the table. # Creating Card Programs Source: https://docs.sudo.africa/docs/creating-card-programs A Card Program is a centralized configuration that governs how a batch or category of cards is issued, funded, and controlled. Each card issued under a program inherits the rules, funding logic, and spending limits defined at the program level, creating operational consistency and significantly reducing manual overhead. Learn how to create card program via the Sudo Dashboard or API. ## 1. Using the API This document defines the technical and operational specification for the Virtual AfriGo Credit Cards program. The program is designed to issue virtual credit cards under the AfriGo network, issued in Nigeria (NGA) and denominated in Naira (NGN). Optional spending controls are supported for enhanced security and card behavior customization. If not defined, a default control will be applied automatically. ```curl curl theme={null} curl --location --request POST 'https://api.sandbox.sudo.cards/card-programs' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer ••••••••••••••••••••••••••••••••••••CREE1M0EwQjgyMjI0NUE3QUM=' \ --data-raw '{ "name": "Virtual AfriGo Credit Cards", "reference": "250605-1051", "description": "For Virtual AfriGo Credit Cards", "status": "active", "debitAccountId": "67974b365c184d20fc340889", "fundingSourceId": "670cece725852ba485d745c7", "issuerCountry": "NGA", "currency": "NGN", "cardBrand": "AfriGo", "cardType": "virtual", "spendingControls": { "channels": { "atm": true, "pos": true, "web": true, "mobile": true }, "allowedCategories": [], "blockedCategories": [], "spendingLimits": [ { "amount": 100000, "interval": "daily", "categories": [] } ] } }' ``` A call to the card program's endpoint returns information about the program that can be subsequently used to create a card. # Creating Cards Source: https://docs.sudo.africa/docs/creating-cards Learn how to create physical and virtual cards via the Sudo Dashboard or API. ## 1. Create a cardholder A cardholder is either an individual or business entity that can be issued a payment card. To get started, create a cardholder with a `name`, `billingAddress`, and `type`. You can include additional information like `KYC details`, `phone number`, and `email address`. ```curl curl theme={null} curl --location --request POST 'https://api.sandbox.sudo.cards/customers' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer ••••••••••••••••••••••••••••••••••••CREE1M0EwQjgyMjI0NUE3QUM=' \ --data-raw '{ "type": "individual", "name": "John Doe", "status": "active", "individual": { "firstName": "John", "lastName": "Doe" }, "billingAddress": { "line1": "4 Barnawa Close", "line2": "Off Challawa Crescent", "city": "Barnawa", "state": "Kaduna", "country": "NG", "postalCode": "800001" } }' ``` A call to the cardholder's endpoint returns information about the cardholder that can be subsequently used to create a card. ## 2. Create a card for a cardholder Create a card and assign it to a cardholder. This request requires the cardholder ID from the previous step, type, card number (if a physical card), currency, and status. More parameters might be required as per your requirements. ```curl Create Card theme={null} curl --location --request POST 'https://api.sandbox.sudo.cards/cards' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer ••••••••••••••••••••••••••••••••••••CREE1M0EwQjgyMjI0NUE3QUM' \ --data-raw '{ "customerId": "5f8b75ef12a06df84bd7aa3a", "type": "physical", "number": "5061000001743021565", "currency": "NGN", "status": "active" }' ``` ```curl Virtual Card Using a Card Program theme={null} curl --location --request POST 'https://api.sandbox.sudo.cards/cards' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer ••••••••••••••••••••••••••••••••••••CREE1M0EwQjgyMjI0NUE3QUM' \ --data-raw '{ "customerId": "670cf0ad25852ba485d7590d", "programId": "6840b5161443c90831ba07a5", "status": "active", "metadata": {} }' ``` ```curl Physical Card Using a Card Program theme={null} curl --location --request POST 'https://api.sandbox.sudo.cards/cards' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer ••••••••••••••••••••••••••••••••••••CREE1M0EwQjgyMjI0NUE3QUM' \ --data-raw '{ "customerId": "670cf0ad25852ba485d7590d", "programId": "6840b5161443c90831ba07a5", "number": "5061000001743021565", "status": "active", "metadata": {} }' ``` A call to the card's endpoint returns information about the card being mapped. In order to maintain PCI Compliance and high card data security, all requests to map or retrieve card details must be passed through the vault endpoint. # Displaying Sensitive Card Data Source: https://docs.sudo.africa/docs/displaying-sensitive-card-data In order to display sensitive card data (card number, CVV2 and default PIN) to a customer, without any of it passing through your systems (which would subject you to PCI compliance requirements), Sudo utilizes a 3rd party service called Secure Proxy. By default, sensitive card data are redacted in the response sent back from the card endpoints Secure Proxy provides a JavaScript library called Secure Proxy Show which allows easy integration with your UI components. Secure Proxy Show offloads the PCI compliance burden by enabling the encrypted transmission of sensitive card data from Secure Proxy directly to your cardholder. The Secure Proxy Show JavaScript library enables you to securely display sensitive data in a webpage while safely isolating that sensitive data from your systems. Secure Proxy Show JavaScript library injects a secure iframe into your HTML. Secure Proxy hosts both the iframe, and the data on secure, compliant servers. **How to Implement** **Step** **1** : Import the Secure Proxy Show Library using the scripts below according to your environment. | Environment | Script | | ----------- | -------------------------------------------------------------------------------------------------------------------- | | Sandbox | `` | | Live | `` | **Step** **2** : Initialize the component `const show = SecureProxy.create();` Vault ID | Environment | ID | | ----------- | ----------- | | Sandbox | `we0dsa28s` | | Live | `vdl2xefo5` | **Step** **3** : Create Card Token. You can do that here: [Generate Card Token](/reference/generate-card-token) . The card token is required in the authorization header of the show js request. **Step** **4**: Provide a valid card identifier `path: '/cards//secure-data/cvv2'` ```html Example of displaying card number, cvv2 and PIN theme={null}

Sensitive Data example

```
# Disputes Source: https://docs.sudo.africa/docs/disputes Customers are generally allowed to log disputes on transactions when they have dispense errors at an ATM, POS, or Online, when they have problems with the quality or delivery of goods and services they ordered or when they notice a fraudulent transaction on their cards. Sudo provides an easy way of reporting such transactions for further investigations. The process usually takes 24-48 hours and all disputes must be reported within 24 hours of making such transactions. To submit a dispute, log in to the Sudo Dashboard or access the disputes API. # Environments Source: https://docs.sudo.africa/docs/environments The Sudo API and Dashboard are available in two environments: | Environment | Sandbox | Production | | ------------- | -------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- | | Dashboard URL | `https://app.sandbox.sudo.cards` | `https://app.sudo.africa` | | API URL | `https://api.sandbox.sudo.cards` | `https://api.sudo.africa` | | Vault URL | `https://vault.sudo.africa` | `https://vault.sudo.cards` | | VGS Script | `` | `` | | Vault ID | `we0dsa28s` | `vdl2xefo5` | The sandbox environment contains special API operations that allow you to easily test and simulate different activities, from account payments to card payments. You can find the full reference under Simulations. # Errors Source: https://docs.sudo.africa/docs/errors Sudo uses conventional HTTP response codes to indicate the success or failure of an API request. In general: Codes in the `2xx` range indicate success. Codes in the `4xx` range indicate an error that failed given the information provided (e.g., a required parameter was omitted, a request failed, etc.). Codes in the `5xx` range indicate an error with Sudo's servers (these are rare). | Code | Description | | ---------------------------------- | ----------------------------------------------------------------------------------------------------- | | 200 - OK | Everything worked as expected. | | 400 - Bad Request | The request was unacceptable, often due to missing a required parameter. | | 401 - Unauthorized | No valid API key was provided. | | 402 - Request Failed | The parameters were valid but the request failed. | | 403 - Forbidden | The API key doesn't have permission to perform the request. | | 404 - Not Found | The requested resource doesn't exist. | | 409 - Conflict | The request conflicts with another request. | | 429 - Too Many Requests | Too many requests hit the API too quickly. We recommend an exponential backoff of your requests. | | 500, 502, 503, 504 - Server Errors | Something went wrong on Sudo's end. These are rare and if they happen, please contact us immediately. | # Fraud Protection Source: https://docs.sudo.africa/docs/fraud-protection Sudo automatically blocks any authorization that looks suspicious. If we decline the authorization attempt as a result of our fraud analysis, the `requestHistory.reason` field on the Authorization is set to `suspected_fraud`. ```javascript javascript theme={null} requestHistory: [ { _id: "5f920639d466350ec7a8f0ff" amount: 505.375 currency: "NGN" approved: false merchantAmount: 500 merchantCurrency: "NGN" reason: "suspected_fraud" createdAt: "2020-10-22T22:22:49.157Z" } ] ``` Think a transaction was declined incorrectly? Please reach out to our support team with the details of the transaction. ## Spending Controls We recommend that you implement a combination of spending limits and merchant category controls on your cards to help limit your exposure in case fraud is attempted. If spendingLimits are not set, a default spending limit is applied to all cards. NGN Cards Single Transaction Limit - NGN20,000 (ATM) | NGN500,000 (POS/WEB) Daily Limit - NGN150,000 (ATM) | NGN500,000 (POS/WEB) USD Cards Single Transaction Limit - USD0.00 (ATM) | USD0.00 (POS/WEB) Daily Limit - USD0.00 (ATM) | USD0.00 (POS/WEB) ## Verification Data For every authorization on Sudo Cards, we pass the verification, transactionMetadata, terminal, and merchant information to your webhook to make final decisions. You get to know the channel used, merchant name, location, and terminal details including the Terminal ID. We also send the card presence and cardholder presence status to you so you can make the right decisions before approving or declining a transaction. ```javascript javascript theme={null} "verification":{ "_id":"5f920639d466350ec7a8f0f1", "billingAddressLine1":"not_provided", "billingAddressPostalCode":"not_provided", "cvv":"match", "expiry":"match", "pin":"match", "threeDSecure":"not_provided", "safeToken":"not_provided", "authentication":"pin" } ... "terminal":{ "_id":"5f920639d466350ec7a8f0ee", "rrn":"201022232227", "stan":"280632", "terminalId":"2058VX16", "terminalOperatingEnvironment":"on_premise", "terminalAttendance":"attended", "terminalType":"pos", "panEntryMode":"magnetic_stripe", "pinEntryMode":"magnetic_stripe", "cardHolderPresence":true, "cardPresence":true } ... "merchant":{ "_id":"5f920639d466350ec7a8f0ed", "category":"6051", "name":"SUDO AFRICA LIMITED", "merchantId":"2058FC017155567", "city":"FC", "state":"LA", "country":"NG", "postalCode":"100001" } ... "transactionMetadata":{ "_id":"5f920639d466350ec7a8f0ef", "channel":"pos", "type":"purchase", "reference":"6050000406201022232227" } ``` Sudo gives you a **4** seconds window to respond to any request. If we do not receive a response from you within such time, the `Authorization` is automatically approved or declined based on your timeout settings in the card funding source. # Funding Sources Source: https://docs.sudo.africa/docs/funding-sources-guide A funding source represents a bank account from which funds are drawn for card transactions. There are 3 types of funding sources used on Sudo. 1. Default Funding Source 2. Account Funding Source 3. Gateway Funding Source These funding sources can be set during card creation or update. Funding sources are different from `debitAccountId` requested during card creation and funding. The `debitAccountId` refers to the `_id` of your business' settlement account that would be charged strictly only during that card creation or funding process. ## Default Funding Source For this funding source, the funds are always taken from the customer's wallet at the point of an authorization. This funding source is used by default during card creation/mapping, if no specific funding source is selected on the dashboard or added in the card object for the endpoint. ## Account Funding Source For this funding source, the funds are always taken from the business' settlement account of the same currency at the point of an authorization. This means that if a business' customer uses a card to purchase something, the business' settlement account would be charged for this, not the customer's wallet. This is especially useful for businesses that wish to have more control over their customers' card while using the dashboard. ## Gateway Funding Source This funding source is also similar to the account funding source, in the sense that, the business' settlement account would also be charged for its customer's transactions. But, the gateway funding source allows businesses to approve or decline transactions in real-time. Therefore, it would be the best case for businesses that wish to work with the API directly, and/or maintain their customers wallet balances on their end. While creating a Gateway Funding Source, if you set Authorized by Default to `true` it means transactions will be approved without waiting for your response. For this funding source, a webhook url must be provided. This is where authorization requests would be sent during real-time authorizations. Click [here](/docs/real-time-authorizations) to find out more on Real-time Authorizations. In order to receive authorization requests, the webhook for the gateway funding source must be added during the funding source creation or update, on the Settings page, under the 'Funding Sources' tab. For every card created or mapped to a customer, a corresponding wallet is also created, regardless of the funding source being used. # Introduction Source: https://docs.sudo.africa/docs/getting-started Welcome to the Sudo API! 🙋🏽‍♂️ Our documentation will guide you throughout your integration, from the basics (authentication, request structure) to using and creating financial products (accounts, cards, payments, etc.). Ready to get started? Sign up [here](https://app.sudo.africa) to receive immediate access to our Sandbox and start building with Sudo. We're excited you're here! We promise you'll be up and running in a jiffy! 🤞🏽 # Lecture Source: https://docs.sudo.africa/docs/lecture We trust you have received the usual lecture from the local System Administrator. It usually boils down to these three things: 1. Respect the privacy of others. 2. Think before you type. 3. With great power comes great responsibility. ✌️ # Merchant Categories Source: https://docs.sudo.africa/docs/merchant-categories A merchant category code (MCC) is a four-digit number assigned by card networks to a business based on the goods or services offered by the business. You can use these categories when creating spending controls to restrict issued cards from being used with certain business types. For easy reference, see the files below. 1. [mcc.js](https://drive.google.com/file/d/1ojizieFs__Bwr7DG_WtEqn7rvQ7KpgFO/view?usp=sharing) 2. [mcc.pdf](https://drive.google.com/file/d/150o-JBHt6jyP3846rIH93mBU-1AuOL01/view?usp=sharing) # Metadata Source: https://docs.sudo.africa/docs/metadata The Sudo API allows you to store useful additional structured information on an object. You can store multiple key-value pairs which will be available on the data object at any time when retrieved. Sudo does not make use of any data you store in the metadata object. Do not store any sensitive information (card details, passwords, personal identification details etc.) as metadata. # Onboarding Source: https://docs.sudo.africa/docs/onboarding ## Checklist Before we migrate you to the production environment (i.e., before you are able to onboard customers and issue live cards), you must complete the following items. 1. Create a Sudo account. Go to [https://app.sudo.africa](https://app.sudo.africa) to create an account if you don't already have one. 2. Setup and explore the sandbox environment (see below). 3. Check out the rest of this [Guide](/docs/getting-started) and [API Reference](/reference/introduction) for more information on Sudo's offerings. 4. Reach out to us by sending an email to `[email protected]` or via the live chat plugin on our websites to discuss any questions, concerns, or clarifications you might have. 5. Submit your business information for Sudo Compliance's approval and production environment activation. 6. Setup production environment (see below). 7. Order physical cards or start generating virtual cards immediately. ## Sandbox Environment Setup 1. Create a Sudo account. Go to [https://app.sudo.africa](https://app.sudo.africa) to create an account if you don't already have one. 2. Create an API Key. This can be done on the Dashboard's Developers page. 3. Create a business account on [SafeHaven](https://safehavenmfb.com) and Connect it to your Sudo account. (Nigerian businesses only & optional on sandbox environment). 4. Create a default settlement account in the currency of your choice. This can be done on the Dashboard's Accounts page or via the API. 5. Create a default funding source. This can be done on the Dashboard's Settings (Funding Sources) page or via the API. 6. Create your first cardholder. See [Create Customer](/reference/create-customer) 7. Use the simulator to generate a test card. See [Generate Test Card](/reference/generate-test-card) 8. Create/Map your test cad to a cardholder. See [Create Card](/reference/create-card) 9. Use the simulator to make your first authorization. See [Simulate Authorization](/reference/simulate-authorization) You've successfully tested Sudo on the Sandbox Environment. You can proceed to complete your integrations before switching to the production environment. ## Production Environment Setup NB: It is assumed that you already have a production-ready Sudo account. If you don't have one, see the Checklist above. 1. Create an API Key. This can be done on the Dashboard's Developers page. 2. Create a business account on [SafeHaven](https://safehavenmfb.com) and Connect it to your Sudo account (Nigerian businesses only). 3. Create a default settlement account in the currency of your choice. This can be done on the Dashboard's Accounts page or via the API. 4. Create a default funding source. This can be done on the Dashboard's Settings (Funding Sources) page or via the API. 5. Order Physical Sudo Cards by sending an email to `[email protected]` or via the live chat plugin on our websites. 6. Proceed to create a real cardholder, create cards, and try a real-life transaction at an ATM, POS, or Web platform. # Pagination Source: https://docs.sudo.africa/docs/pagination Sudo supports fetch of all top-level API resources like Customers, Cards, Authorizations, Transactions, etc. These endpoints share a common structure, taking at least these two parameters: `page` and `limit`. By default, the page is set to `0` and a limit of `25`. You can fetch a maximum of `100` records at once. The resulting response will always include a `pagination` object with the `total` records count, the number of `pages`, the current `page`, and the `limit` set. ```javascript javascript theme={null} { "statusCode": 200, "message": "Cards fetched successfully.", "data": [ {.....}, {.....}, {.....} ], "pagination": { "total": 1, "pages": 1, "page": "0", "limit": "25" } } ``` # Physical Cards Source: https://docs.sudo.africa/docs/physical-cards A physical card is a payment instrument that enables users to conduct transactions at merchant locations. The card can either be used physically on ATMs, POS, or virtually online. The Sudo API enables you to control your cards programmatically, build your own features, securely integrate with other services, and create new world-class experiences. A card stores data that is necessary for a merchant to make an authorization request. An authorization request occurs when a cardholder attempts a withdrawal at an automated teller machine (ATM), presents the card to a merchant at a physical point of sale (POS) or enters the card information into a form for online purchase. Cards store multiple pieces of data necessary to complete a transaction, including the following: **Name** - cardholder’s name. **Primary Account Number (PAN)** - a 16-19 digits unique identifier that appears on the front or back of the card; identifies the card network, issuer, and cardholder account. **Expiry Date** - the date when the card expires. **Card Verification Value (CVV2)** - a 3 digits verification number that appears on the back of the card, usually used for “card not present (online)” transactions. **Personal Identification Number (PIN)** - a 4 digits number used to authorize transactions either online or offline. The Sudo API allows you to configure a phone number and email address during card setup to enroll for SafeToken (OTP) or 3D Secure. This is useful to protect the card against online card fraud where an OTP is sent to the customer's mobile phone/email address to authorize any transaction before it is authorized. # Postman Collection Source: https://docs.sudo.africa/docs/postman-collection You can interact immediately with the Sudo API via Postman. To get started, click the **Run in Postman** button below to import our Postman Collection. For more information, see the following under the Postman help: [Importing Data into Postman](https://learning.postman.com/docs/getting-started/importing-and-exporting-data/#importing-data-into-postman). [Run in Postman](https://app.getpostman.com/run-collection/9873298-0fd9b161-1320-440b-99a0-8a20239e1d51?action=collection%2Ffork\&collection-url=entityId%3D9873298-0fd9b161-1320-440b-99a0-8a20239e1d51%26entityType%3Dcollection) The Sudo Postman collection uses [Postman environment variables](https://learning.getpostman.com/docs/postman/environments_and_globals/intro_to_environments_and_globals/) to simplify each API request. More information on managing Postman environments can be found at [Setting up an environment with variables](https://learning.getpostman.com/docs/postman/environments_and_globals/manage_environments/). You will need to set the following environment variables. | Variable | Value | | -------------- | ------------------------------------------------------------------------------------------------------------------------ | | `baseUrl` | Initially set to the sandbox environment API endpoint. See [Environments](/docs/environments) for environment endpoints. | | `apiKey` | Your API Key. You can create it from the API Keys page on the Sudo Dashboard. | | `vaultBaseUrl` | See [Environments](/docs/environments) for Vault URL. | # Real-time Authorizations Source: https://docs.sudo.africa/docs/real-time-authorizations The Sudo API allows you to set up an asynchronous webhook to approve or decline transactions in real-time. Your webhook endpoint can be set up by creating a funding source and setting that funding source while creating a new card or updating an existing one. Sudo creates and sends you an `authorization.request` event to approve or decline the authorization. ## Authorization Requests Your webhook must approve or decline each authorization request sent by responding with the appropriate response body. If Sudo does not receive a response from you within **4 seconds**, the Authorization is automatically approved or declined based on your timeout settings in the card funding source. If your main wallet balance does not have enough funds for the incoming authorization, it is automatically declined and you will not receive an event on your webhook. ```javascript javascript theme={null} let bodyParser = require("body-parser"); let cors = require("cors"); let express = require("express"); let app = express(); app.use(bodyParser()); app.use(cors()); app.get('/', (req, res) => { return res.json({ foo: "Bar!" }); }); app.post('/sudo/jitgateway', (req, res) => { if(req.body.type === "card.balance") { res.status(200); res.json({ statusCode: 200, responseCode: "00", data: { balance: 40000 } }); }else if(req.body.type === "authorization.request") { res.status(200); res.json({ statusCode: 200, responseCode: "00", data: { metadata: { foo: "bar" } } }); }else { res.status(403); res.json({ statusCode: 403, responseCode: "96", message: "Error" }); } }); app.listen(process.env.PORT || 3000, () => { console.log("Server's Up!"); }); ``` In the example above, we set up an express server and exposed an endpoint `/sudo/jitgateway` to accept POST requests. This is listening to two event `card.balance` and `authorization.request`. Responding to the webhook request with a status 200 and a JSON body with `statusCode` 200 approves the authorization request. ```javascript javascript theme={null} { "statusCode":200, "responseCode":"00", "data":{ "metadata":{ "foo":"bar" } } } ``` Optionally, you can add some metadata to the authorization request to be passed to the transaction eventually approved or an ISO 8583 `responseCode` to be returned for the authorization. ```javascript javascript theme={null} { "statusCode":400, "responseCode":"51" } ``` The example response body above rejects a transaction with a response code `51` indicating `Insufficient Balance`. ## Balance Requests When a balance request is sent to your webhook, take a look at the user and account details then respond with the appropriate body indicating the user's spendable balance. ```json json theme={null} { "business":"61cc42a046996fb949322a3e", "data":{ "object":{ "_id":"61ce491d9400b25b439ae426", "business":"61cc42a046996fb949322a3e", "customer":{ "_id":"61ccdc228ce8d1fe6dceacf9", "business":"61cc42a046996fb949322a3e", "type":"individual", "name":"John Doe", "status":"active", "individual":{ "firstName":"John", "lastName":"Doe", "_id":"61ccdc228ce8d1fe6dceacfa" }, "billingAddress":{ "line1":"4 Barnawa Close", "line2":"Off Challawa Crescent", "city":"Barnawa", "state":"Kaduna", "country":"Nigeria", "postalCode":"800243", "_id":"61ccdc228ce8d1fe6dceacfb" }, "isDeleted":false, "createdAt":"2021-12-29T22:07:30.301Z", "updatedAt":"2021-12-29T22:07:30.301Z", "__v":0 }, "account":{ "_id":"61ce491d9400b25b439ae424", "business":"61cc42a046996fb949322a3e", "type":"wallet", "currency":"NGN", "accountName":"SUDO / JOHN DOE", "bankCode":"999240", "accountType":"Current", "accountNumber":"8016065912", "currentBalance":0, "availableBalance":0, "provider":"SafeHaven", "providerReference":"61ce491d3153ed001e8425e2", "referenceCode":"subacc_1640909083619", "isDefault":true, "isDeleted":false, "createdAt":"2021-12-31T00:04:45.657Z", "updatedAt":"2021-12-31T00:04:45.657Z", "__v":0 }, "fundingSource":{ "_id":"61ccdc118ce8d1fe6dceacf0", "business":"61cc42a046996fb949322a3e", "type":"gateway", "status":"active", "jitGateway":{ "url":"https://AfraidRoughRedundancy.aminubakori.repl.co/sudo/jitgateway", "authorizationHeader":"Bearer ACME_TOKEN", "authorizeByDefault":false, "_id":"61ccdc118ce8d1fe6dceacf1" }, "isDefault":false, "isDeleted":false, "createdAt":"2021-12-29T22:07:13.392Z", "updatedAt":"2021-12-29T22:07:13.392Z", "__v":0 }, "type":"physical", "brand":"Verve", "currency":"NGN", "maskedPan":"444444******4430", "expiryMonth":"01", "expiryYear":"2025", "status":"active", "spendingControls":{ "channels":{ "atm":true, "pos":true, "web":true, "mobile":true, "_id":"61ce491d9400b25b439ae428" }, "allowedCategories":[ ], "blockedCategories":[ ], "spendingLimits":[ { "amount":1000, "interval":"daily", "categories":[ ], "_id":"61ce491d9400b25b439ae429" } ], "_id":"61ce491d9400b25b439ae427" }, "isDeleted":false, "createdAt":"2021-12-31T00:04:45.840Z", "updatedAt":"2021-12-31T00:04:45.840Z", "__v":0 }, "_id":"61d8f3cec46018873905a908" }, "type":"card.balance", "pendingWebhook":false, "webhookArchived":false, "createdAt":1641608142, "_id":"61d8f3cec46018873905a907" } ``` ## Authorization Requests When an authorization request is sent to your webhook, the `amount` requested is stored in `pendingRequest` object. ```json json theme={null} { "environment":"development", "business":"61cc42a046996fb949322a3e", "data":{ "object":{ "_id":"61d8f42bc46018873905a955", "business":"61cc42a046996fb949322a3e", "customer":{ "_id":"61ccdc228ce8d1fe6dceacf9", "business":"61cc42a046996fb949322a3e", "type":"individual", "name":"John Doe", "status":"active", "individual":{ "firstName":"John", "lastName":"Doe", "_id":"61ccdc228ce8d1fe6dceacfa" }, "billingAddress":{ "line1":"4 Barnawa Close", "line2":"Off Challawa Crescent", "city":"Barnawa", "state":"Kaduna", "country":"Nigeria", "postalCode":"800243", "_id":"61ccdc228ce8d1fe6dceacfb" }, "isDeleted":false, "createdAt":"2021-12-29T22:07:30.301Z", "updatedAt":"2021-12-29T22:07:30.301Z", "__v":0 }, "account":{ "_id":"61ce491d9400b25b439ae424", "business":"61cc42a046996fb949322a3e", "type":"wallet", "currency":"NGN", "accountName":"SUDO / JOHN DOE", "bankCode":"999240", "accountType":"Current", "accountNumber":"8016065912", "currentBalance":0, "availableBalance":0, "provider":"SafeHaven", "providerReference":"61ce491d3153ed001e8425e2", "referenceCode":"subacc_1640909083619", "isDefault":true, "isDeleted":false, "createdAt":"2021-12-31T00:04:45.657Z", "updatedAt":"2021-12-31T00:04:45.657Z", "__v":0 }, "card":{ "_id":"61ce491d9400b25b439ae426", "business":"61cc42a046996fb949322a3e", "customer":"61ccdc228ce8d1fe6dceacf9", "account":"61ce491d9400b25b439ae424", "fundingSource":{ "_id":"61ccdc118ce8d1fe6dceacf0", "business":"61cc42a046996fb949322a3e", "type":"gateway", "status":"active", "jitGateway":{ "url":"https://AfraidRoughRedundancy.aminubakori.repl.co/sudo/jitgateway", "authorizationHeader":"Bearer ACME_TOKEN", "authorizeByDefault":false, "_id":"61ccdc118ce8d1fe6dceacf1" }, "isDefault":false, "isDeleted":false, "createdAt":"2021-12-29T22:07:13.392Z", "updatedAt":"2021-12-29T22:07:13.392Z", "__v":0 }, "type":"physical", "brand":"Verve", "currency":"NGN", "maskedPan":"444444******4430", "expiryMonth":"01", "expiryYear":"2025", "status":"active", "spendingControls":{ "channels":{ "atm":true, "pos":true, "web":true, "mobile":true, "_id":"61ce491d9400b25b439ae428" }, "allowedCategories":[ ], "blockedCategories":[ ], "spendingLimits":[ { "amount":1000, "interval":"daily", "categories":[ ], "_id":"61ce491d9400b25b439ae429" } ], "_id":"61ce491d9400b25b439ae427" }, "isDeleted":false, "createdAt":"2021-12-31T00:04:45.840Z", "updatedAt":"2021-12-31T00:04:45.840Z", "__v":0 }, "amount":0, "fee":5, "vat":0.375, "approved":false, "currency":"NGN", "status":"pending", "authorizationMethod":"online", "balanceTransactions":[ ], "merchantAmount":20, "merchantCurrency":"NGN", "merchant":{ "category":"6010", "name":"SUDO SIMULATOR", "merchantId":"SUDOSIMULATOR01", "city":"JAHI", "state":"ABUJA", "country":"NG", "postalCode":"100001", "_id":"61d8f42bc46018873905a956" }, "terminal":{ "rrn":"126769857082", "stan":"192208", "terminalId":"3SUDOSIM", "terminalOperatingEnvironment":"on_premise", "terminalAttendance":"unattended", "terminalType":"ecommerce", "panEntryMode":"keyed_in", "pinEntryMode":"keyed_in", "cardHolderPresence":true, "cardPresence":true, "_id":"61d8f42bc46018873905a957" }, "transactionMetadata":{ "channel":"web", "type":"purchase", "reference":"0123456783126769857082", "_id":"61d8f42bc46018873905a958" }, "pendingRequest":{ "amount":25.375, "currency":"NGN", "merchantAmount":20, "merchantCurrency":"NGN", "_id":"61d8f42bc46018873905a959" }, "requestHistory":[ ], "verification":{ "billingAddressLine1":"not_provided", "billingAddressPostalCode":"not_provided", "cvv":"match", "expiry":"match", "pin":"match", "threeDSecure":"not_provided", "safeToken":"not_provided", "authentication":"pin", "_id":"61d8f42bc46018873905a95a" }, "isDeleted":false, "createdAt":"2022-01-08T02:17:15.075Z", "updatedAt":"2022-01-08T02:17:15.075Z", "feeDetails":[ { "contract":"61a18b8a4ddab599d20344a7", "currency":"NGN", "amount":5, "description":"Verve Card Authorization Fee", "_id":"61d8f42bc46018873905a95b" } ], "__v":0 }, "_id":"61d8f42bc46018873905a96f", "changes":" {\n- amount: 0\n+ amount: 25.375\n- approved: false\n+ approved: true\n- status: \"pending\"\n+ status: \"approved\"\n- pendingRequest: {\n- amount: 25.375\n- currency: \"NGN\"\n- merchantAmount: 20\n- merchantCurrency: \"NGN\"\n- _id: \"61d8f42bc46018873905a959\"\n- }\n+ pendingRequest: null\n requestHistory: [\n+ {\n+ amount: 25.375\n+ currency: \"NGN\"\n+ approved: true\n+ merchantAmount: 20\n+ merchantCurrency: \"NGN\"\n+ reason: \"webhook_approved\"\n+ createdAt: \"2022-01-08T02:17:15.075Z\"\n+ _id: \"61d8f42cc46018873905a979\"\n+ }\n ]\n }\n" }, "type":"authorization.request", "pendingWebhook":false, "webhookArchived":false, "createdAt":1641608235, "_id":"61d8f42bc46018873905a96e" } ``` The top-level amount in the request is set to 0 and approved is false. Once you respond to the request, the top-level amount reflects the total amount approved or declined, the approved field is updated, and pendingRequest is set to null. # Setup Webhooks Source: https://docs.sudo.africa/docs/setup-webhooks Creating a Webhook endpoint can be easily done on your dashboard by following these steps. 2880 1. Log in to the Sudo Dashboard and click on **Developers**. 2. Click on **Webhooks** in the **Developers** navigation menu seen horizontally on your screen. 3. Click on the **Create Webhook** button, then fill in the required details for your webhook. 4. Once you're done, click on the **Create Webhook** button on the modal. You can change the Webhook URL, Authorization Token, or enable/disable a webhook by clicking on the Webhook entry on the Developers Webhooks page. # Sign Up Source: https://docs.sudo.africa/docs/sign-up To get started with Sudo, you will need to set up a Business/Developer account. ## 1. Register a Sudo Account 2880 Go to [https://app.sudo.africa](https://app.sudo.africa) and provide your details to create a free account. You'll have access to our sandbox environment instantly. ## 2. Integration Quickstart **2.1. API Integration** Quickly get started building on the Sudo APIs to create exceptional new experiences. See our [API References](/reference). **2.2. No Code** The Sudo Dashboard is designed to be an easy tool to create and manage your products with zero-coding skills required. Go to [https://app.sudo.africa](https://app.sudo.africa) to access your dashboard. # Spending Controls Source: https://docs.sudo.africa/docs/spending-controls Spending controls can be used to block business categories or set spending limits (e.g NGN1,000 per authorization or NGN30,000 per month). This can be applied to cards by setting the `spendingControls` object at creation or by updating it later. If you're using spending controls with real-time authorizations, spending controls run first and decline a purchase before the `authorization.request` event is sent to you, resulting in a declined authorization request. ## Spending Control Object | Field | Type | Description | | ----------------- | ------ | -------------------------------------------------------------------------------------- | | channels | Object | Channels selection where card can be used. | | allowedCategories | Array | List of categories of authorizations to allow. All other categories will be blocked. | | blockedCategories | Array | List of categories of authorizations to decline. All other categories will be allowed. | | spendingLimits | Array | List of objects that specify amount-based rules. | ## Channels To limit where the card can be used, set either `true` or `false` on the following variables. | Field | Type | Description | | ------ | ------- | ----------------------------------------------------------- | | atm | Boolean | Automated teller machine (ATM) withdrawals and transactions | | pos | Boolean | Point of sales (POS) purchases | | web | Boolean | Online purchases | | mobile | Boolean | Mobile purchases | ## Spending Limits To limit the amount of money that can be spent, set `spendingLimits` within the `spendingControls` object to a list of objects with the following variables. | Field | Type | Description | | ---------- | ------ | ------------------------------------------------------------------------------------------------------------------------------- | | amount | Number | Maximum amount allowed to spend per interval set. | | interval | Enum | Time interval to which the amount applies. This can either be per\_authorization, daily, weekly, monthly, yearly, or all\_time. | | categories | Array | List of categories of authorizations to limit. Leaving this field empty will apply the limit to all categories. | If `spendingLimits` are not set, a default spending limit is applied to all cards. **NGN Cards** Single Transaction Limit - NGN20,000 (ATM) | NGN500,000 (POS/WEB) Daily Limit - NGN150,000 (ATM) | NGN500,000 (POS/WEB) **USD Cards** Single Transaction Limit - USD0.00 (ATM) | USD0.00 (POS/WEB) Daily Limit - USD0.00 (ATM) | USD0.00 (POS/WEB) **Limit a card's monthly spend** ```curl curl theme={null} curl --location --request PUT 'https://api.sandbox.sudo.africa/cards/5f45f4d018ccd82774de7d07' \ --header 'Authorization: Bearer ••••••••••••••••••••••••••••••••••••CREE1M0EwQjgyMjI0NUE3QUM=' \ --header 'Content-Type: application/json' \ --data-raw '{ "spendingControls": { "channels": { "atm": true, "pos": true, "web": true, "mobile": true }, "allowedCategories": [], "blockedCategories": [], "spendingLimits": [{ "amount": 100000, "interval": "monthly" }] } }' ``` **Limit a card's daily spend for specific categories** ````curl curl theme={null} ```bash curl --location --request PUT 'https://api.sandbox.sudo.africa/cards/5f45f4d018ccd82774de7d07' \ --header 'Authorization: Bearer ••••••••••••••••••••••••••••••••••••CREE1M0EwQjgyMjI0NUE3QUM=' \ --header 'Content-Type: application/json' \ --data-raw '{ "spendingControls": { "channels": { "atm": true, "pos": true, "web": true, "mobile": true }, "allowedCategories": [], "blockedCategories": [], "spendingLimits": [{ "amount": 3000, "interval": "daily", "categories": ["6011"] **** }] } }' ``` ```` # Testing Source: https://docs.sudo.africa/docs/testing Sudo provides an easy way to generate cards and simulate transactions in a sandbox environment. This allows you to test your integrations and ensure that everything works as expected before going live making real transactions. You can simulate card transactions using your sandbox credentials as seen on your dashboard with these steps: 1. Fund your default account. 2. Create a funding source. 3. Create a cardholder. 4. Generate a sample card. 5. Create/Map card to a cardholder. 6. Simulate your first card transaction. ## 1. Fund your default account. To fund your default account, get the list of all accounts and note the `_id`, `accountNumber`, and `bankCode` of the default account. Then proceed to make a transfer to the account in LIVE or fund using the sandbox simulator. ```curl curl theme={null} curl --location --request GET 'https://api.sandbox.sudo.cards/accounts?page=0&limit=25' \ --header 'Authorization: Bearer ••••••••••••••••••••••••••••••••••••CREE1M0EwQjgyMjI0NUE3QUM=' \ ``` Then proceed to make funding using the sandbox simulator. ```curl curl theme={null} curl --location --request POST 'https://api.sandbox.sudo.cards/accounts/simulator/fund' \ --header 'Authorization: Bearer ••••••••••••••••••••••••••••••••••••CREE1M0EwQjgyMjI0NUE3QUM=' \ --header 'Content-Type: application/json' \ --data-raw '{ "accountId": "{{defaultAccountId}}", "amount": 5000.00 }' ``` ## 2. Create a funding source. When you set `authorizeByDefault` to `true`, all transactions get approved without waiting for your response. We will re-attempt to send the completed authorization request to your webhook after the transaction is completed. ```curl curl theme={null} curl --location --request POST 'https://api.sandbox.sudo.cards/fundingsources' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer ••••••••••••••••••••••••••••••••••••CREE1M0EwQjgyMjI0NUE3QUM=' \ --data-raw '{ "type": "gateway", "status": "active", "jitGateway": { "url": "https://api.domain.tld/sudo/jitgateway", "authorizationHeader": "Bearer MY_TOKEN", "authorizeByDefault": false } }' ``` ## 3. Create a cardholder. A cardholder is either an individual or business entity that can be issued a payment card. To get started, create a cardholder with name, billingAddress and type. You can include additional information like KYC details, phone number, and email address. See Customers under the API Reference. ```curl curl theme={null} curl --location --request POST 'https://api.sandbox.sudo.africa/customers' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer ••••••••••••••••••••••••••••••••••••CREE1M0EwQjgyMjI0NUE3QUM=' \ --data-raw '{ "type": "individual", "name": "John Doe", "status": "active", "individual": { "firstName": "John", "lastName": "Doe" }, "billingAddress": { "line1": "4 Barnawa Close", "line2": "Off Challawa Crescent", "city": "Barnawa", "state": "Kaduna", "country": "NG", "postalCode": "800001" } }' ``` ## 4. Generate a sample card. For physical cards, a card is needed before mapping. To generate a card on the sandbox environment, send a request to the card generation endpoint. ```curl curl theme={null} curl --location --request GET 'https://api.sandbox.sudo.cards/cards/simulator/generate' \ --header 'Authorization: Bearer ••••••••••••••••••••••••••••••••••••CREE1M0EwQjgyMjI0NUE3QUM=' \ ``` ## 5. Create/Map card to a cardholder. This request requires the cardholder ID from the previous step, type, card number (if a physical card), currency, and status. ```curl curl theme={null} curl --location --request POST 'https://vault.sandbox.sudo.cards/cards' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer ••••••••••••••••••••••••••••••••••••CREE1M0EwQjgyMjI0NUE3QUM=' \ --data-raw '{ "customerId": "5f8b75ef12a06df84bd7aa3a", "type": "physical", "number": "5061000001743021565", "currency": "NGN", "status": "active" }' ``` 6. Make your first card transaction. To make your first card transaction, proceed to simulate a transaction using the simulation endpoint. Proceed to make your first transaction at an ATM, POS, or Online using your card. ```curl curl theme={null} curl --location --request POST 'https://api.sudo.africa/cards/simulator/authorization' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer ••••••••••••••••••••••••••••••••••••CREE1M0EwQjgyMjI0NUE3QUM=' \ --data-raw '{ "cardId": "5f90f250349360504cf82619", "channel": "web", "type": "purchase", "amount": 3500, "currency": "NGN", "merchant": { "category": "7399", "merchantId": "000000001", "name": "Acme Inc", "city": "Barnawa", "state": "KD", "country": "NG" } }' ``` # Transactions Source: https://docs.sudo.africa/docs/transactions Once an authorization request is approved, the status on the authorization is updated to `pending`, and the `authorization.updated` webhook event is sent. The amount is deducted from your default available balance. A transaction is then created and the `status` of the authorization is set to `closed`. ## Refunds and Reversals When a dispute is raised on a transaction or a reversal is initiated by the switching network, we create a new transaction it's `type` is set to `refund`, and all amounts in positive. We keep every other detail as it is in the original transaction including the authorization that leads to the refund. We also send a `transaction.refund` event with the details of the refunded transaction to your webhook. ````javascript javascript theme={null} { ... "amount": 10, "fee": 5, "vat": 0.375, "feeDetails": [ { "_id": "5f9217807f397914cb365101", "contract": "5f919ef0d466350ec7a8f0b7", "currency": "NGN", "amount": 5, "description": "Naira Card Authorization Fee" } ], "currency": "NGN", "type": "refund", "merchantAmount": 10, "merchantCurrency": "NGN", "merchant": { "_id": "5f9217807f397914cb3650e8", "category": "7399", "name": "PayantTechnolog/FLW3518", "merchantId": "IPG000000000003", "city": "interswitchde", "state": "LA", "country": "NG", "postalCode": "100001" }, "terminal": { "_id": "5f9217807f397914cb3650e9", "rrn": "001249814944", "stan": "280648", "terminalId": "3IPG0001", "terminalOperatingEnvironment": "off_premise", "terminalAttendance": "unattended", "terminalType": "adminstrative_terminal", "panEntryMode": "unknown", "pinEntryMode": "unknown", "cardHolderPresence": false, "cardPresence": false }, "transactionMetadata": { "_id": "5f9217807f397914cb3650ea", "channel": "atm", "type": "purchase", "reference": "6050000406001249814944" }, "isDeleted": false, "createdAt": "2020-10-23T00:56:16.113Z", "updatedAt": "2020-10-23T00:56:16.113Z", "__v": 0 } ``` ```` # Virtual Cards Source: https://docs.sudo.africa/docs/virtual-cards Just like a physical card, a virtual card is a payment instrument that enables users to conduct transactions virtually online. You can retrieve virtual card details via the Dashboard or via the API. PCI-DSS rules protect cardholder data. For PCI-DSS compliance, we recommend limiting retrieval of virtual card information to the dashboard. You can retrieve both the full unredacted card number and CVV2 from the API. For security reasons, these fields will be omitted unless you explicitly request them with the `reveal` property. ```curl curl theme={null} curl --location --request GET 'https://vault.sandbox.sudo.cards/cards/5f8b75ef12a06df84bd7aa3a?reveal=true' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer ••••••••••••••••••••••••••••••••••••CREE1M0EwQjgyMjI0NUE3QUM=' \ ``` In order to maintain PCI Compliance and high card data security, all requests to map or retrieve card details must be passed through the vault endpoint. # Webhooks Source: https://docs.sudo.africa/docs/webhooks # Authorization Code Source: https://docs.sudo.africa/docs/webhooks/authorization-code This webhook is triggered when an authorization code is generated for a card. The event type is `authorization.code` The `data.object.code` field contains the authorization code, and `data.object.card` carries the card the code was issued for. ```json json theme={null} { "_id": "6624b1f0c3a94b1a2d8f5e11", "environment": "production", "business": "5f3a1b2c4d5e6f7a8b9c0d1e", "data": { "object": { "code": "734715", "card": { "_id": "a50c294e-7482-4c79-9bcf-7e9eb1c5e30b", "business": "5f3a1b2c4d5e6f7a8b9c0d1e", "customer": { "_id": "62b1c9f4e8d7a6b5c4d3e2f1", "business": "5f3a1b2c4d5e6f7a8b9c0d1e", "type": "individual", "name": "John Doe", "phoneNumber": "+2348012345678", "emailAddress": "john.doe@example.com", "status": "active", "individual": {}, "billingAddress": { "line1": "12 Admiralty Way", "city": "Lekki", "state": "Lagos", "country": "NG", "postalCode": "105102" }, "isDeleted": false, "createdAt": "2024-01-15T10:22:41.000Z", "updatedAt": "2024-01-15T10:22:41.000Z", "__v": 0 }, "account": { "_id": "62b1ca10e8d7a6b5c4d3e2f5", "business": "5f3a1b2c4d5e6f7a8b9c0d1e", "customer": "62b1c9f4e8d7a6b5c4d3e2f1", "type": "wallet", "currency": "USD", "accountName": "John Doe", "accountType": "current", "accountNumber": "1000123456", "currentBalance": 250.75, "availableBalance": 250.75, "provider": "SudoHUSMC", "isDefault": true, "isDeleted": false, "createdAt": "2024-01-15T10:22:42.000Z", "updatedAt": "2025-04-24T06:20:11.000Z", "__v": 0 }, "fundingSource": { "_id": "61a0b2c3d4e5f60718293a4b", "business": "5f3a1b2c4d5e6f7a8b9c0d1e", "type": "gateway", "status": "active", "jitGateway": { "url": "https://api.yourcompany.com/sudo/jit", "authorizationHeader": "Bearer ", "authorizeByDefault": false }, "isDefault": true, "isDeleted": false, "createdAt": "2023-11-26T09:04:00.000Z", "updatedAt": "2023-11-26T09:04:00.000Z", "__v": 0 }, "type": "virtual", "brand": "MasterCard", "currency": "USD", "maskedPan": "539983******4021", "last4": "4021", "expiryMonth": "07", "expiryYear": "2028", "status": "active", "metadata": { "employeeId": "EMP-2291" }, "spendingControls": { "channels": { "atm": false, "pos": true, "web": true, "mobile": true }, "allowedCategories": [], "blockedCategories": [], "spendingLimits": [ { "amount": 1000, "interval": "monthly", "categories": [] } ] }, "is2FAEnabled": true, "is2FAEnrolled": true, "isDefaultPINChanged": true, "disposable": false, "isDigitalized": false, "failedTransactionCount": 0, "minimumBalance": 0, "version": "v1", "isDeleted": false, "createdAt": "2024-02-01T08:15:33.000Z", "updatedAt": "2025-04-24T06:22:58.000Z", "__v": 0 } } }, "type": "authorization.code", "pendingWebhook": true, "webhookArchived": false, "createdAt": 1745475786, "__v": 0 } ``` # Balance Enquiry Source: https://docs.sudo.africa/docs/webhooks/balance-enquiry This webhook request is triggered whenever balance enquiry is performed on a card. The event type is `card.balance` This type of webhook is only applicable for Cards using the **Gateway Funding Source** When you receive this type of event, you're expected to respond to the webhook containing the card balance. ## Request Payload ```json json theme={null} { "business": "xxxxxxxxsxxxxxxx", "data": { "object": { "_id": "61e02652bdf6466e4ff94921", "business": "xxxxxxxxsxxxxxxx", "customer": { "_id": "61deecfb51eac6c9d0a49d54", "business": "xxxxxxxxsxxxxxxx", "type": "individual", "name": "Shamsuddeen Omacy", "status": "active", "individual": { "firstName": "Shamsuddeen", "lastName": "Omacy", "_id": "61deecfb51eac6c9d0a49d55" }, "billingAddress": { "line1": "4 Barnawa Close", "line2": "Off Challawa Crescent", "city": "Barnawa", "state": "Kaduna", "country": "Nigeria", "postalCode": "800243", "_id": "61deecfb51eac6c9d0a49d56" }, "isDeleted": false, "createdAt": "2024-01-12T15:00:11.745Z", "updatedAt": "2024-01-12T15:00:11.745Z", "__v": 0 }, "account": { "_id": "61e02652bdf6466e4ff9491f", "business": "xxxxxxxxsxxxxxxx", "type": "wallet", "currency": "NGN", "accountName": "BITAKO / SHAMSUDDEEN OMACY", "bankCode": "999240", "accountType": "Current", "accountNumber": "8017418065", "currentBalance": 0, "availableBalance": 0, "provider": "SafeHaven", "providerReference": "61e026526c14e4001ec12ea4", "referenceCode": "subacc_1642079825141", "isDefault": true, "isDeleted": false, "createdAt": "2024-11-13T13:17:06.606Z", "updatedAt": "2024-11-13T13:17:06.606Z", "__v": 0 }, "fundingSource": { "_id": "61deec9a5454549d0a49d04", "business": "xxxxxxxxsxxxxxxx", "type": "gateway", "status": "active", "jitGateway": { "url": "", "authorizationHeader": "Bearer ", "authorizeByDefault": false, "_id": "61deec9a51eac6c9d0a49d05" }, "isDefault": false, "isDeleted": false, "createdAt": "2024-01-12T14:58:34.348Z", "updatedAt": "2024-01-12T14:58:34.348Z", "__v": 0 }, "type": "virtual", "brand": "Verve", "currency": "NGN", "maskedPan": "507874******5244", "expiryMonth": "11", "expiryYear": "2027", "status": "active", "spendingControls": { "channels": { "atm": true, "pos": true, "web": true, "mobile": true, "_id": "61e02652bdf6466e4ff94923" }, "allowedCategories": [], "blockedCategories": [], "spendingLimits": [{ "amount": 500, "interval": "daily", "categories": [], "_id": "61e02652bdf6466e4ff94924" }], "_id": "61e02652bdf6466e4ff94922" }, "isDeleted": false, "createdAt": "2024-11-13T13:17:06.808Z", "updatedAt": "2024-11-13T13:17:06.808Z", "__v": 0 }, "_id": "61e0282e709508625af2fad6" }, "type": "card.balance", "pendingWebhook": false, "webhookArchived": false, "createdAt": 1742080302, "_id": "61e0282e709508625af2fad5" } ``` The field `data.object._id` identifies the Card ID ## Response Sample ```json Approve theme={null} { "statusCode": 200, "data": { "responseCode": "00", "balance": 1234 } } ``` # Authorization Request Source: https://docs.sudo.africa/docs/webhooks/card-authorization This webhook request is triggered whenever a transaction is attempted on a card. The event type is `authorization.request` This type of webhook is only applicable for Cards using the **Gateway Funding Source** When you receive this type of event, you're expected to respond to the webhook, either to authorize or decline the transaction. ## Response Sample ```json Approve theme={null} { "statusCode": 200, "data": { "responseCode": "00" } } ``` ```json Decline (Insuffient Funds) theme={null} { "statusCode": 400, "data": { "responseCode": "51" } } ``` Please be advised to use [ISO 8583 Response Codes](https://en.wikipedia.org/wiki/ISO_8583#Response_code) ## Request Payload ```json json theme={null} { "environment": "development", "business": "670cec9d25852ba485d74273", "data": { "object": { "_id": "67af0942376903b183b034b4", "business": "670cec9d25852ba485d74273", "customer": { "_id": "670cf0ad25852ba485d7590d", "business": "670cec9d25852ba485d74273", "type": "individual", "name": "Shamsuddeen Omacy", "phoneNumber": "+234 801234567", "emailAddress": "john@doe.com", "status": "active", "individual": { "firstName": "Shamsuddeen", "lastName": "Omacy", "dob": "1996-10-23T10:20:11.998Z", "identity": { "type": "BVN", "number": "3434343e434", "_id": "670cf0ad25852ba485d7590f" }, "documents": { "_id": "670cf0ad25852ba485d75910" }, "_id": "670cf0ad25852ba485d7590e" }, "billingAddress": { "line1": "Ademola Ade. Cres.", "line2": "", "city": "Wuse 2", "state": "FCT", "country": "Nigeria", "postalCode": "662222", }, "isDeleted": false, "createdAt": "2024-10-14T10:21:33.674Z", "updatedAt": "2024-10-14T10:21:33.674Z", }, "account": { "_id": "670cf0d425852ba485d75aa3", "business": "670cec9d25852ba485d74273", "type": "wallet", "currency": "NGN", "accountName": "SUDO / Shamsuddeen Omacy", "bankCode": "999240", "accountType": "Current", "accountNumber": "8022418098", "currentBalance": 0, "availableBalance": 0, "provider": "SafeHaven", "providerReference": "670cf0d33011ac0024537c52", "referenceCode": "subacc_1728901330971", "reloadable": true, "isDefault": true, "isDeleted": false, "createdAt": "2024-10-14T10:22:12.442Z", "updatedAt": "2024-10-14T10:22:12.442Z", "charges": [], "__v": 0 }, "card": { "_id": "670cf0d525852ba485d75ab9", "business": "670cec9d25852ba485d74273", "customer": "670cf0ad25852ba485d7590d", "account": "670cf0d425852ba485d75aa3", "fundingSource": { "_id": "670cece725852ba485d745c7", "business": "670cec9d25852ba485d74273", "type": "gateway", "status": "active", "jitGateway": { "url": "https://webhook.site/07a630d9-ea61-47bc-a14e-e7e7857bea6e", "authorizationHeader": "***", "authorizeByDefault": false, "_id": "670cece725852ba485d745c8" }, "isDefault": false, "isDeleted": false, "createdAt": "2024-10-14T10:05:27.926Z", "updatedAt": "2025-02-14T09:12:43.761Z", "__v": 0 }, "type": "virtual", "brand": "Verve", "currency": "NGN", "maskedPan": "506321******3531", "expiryMonth": "06", "expiryYear": "25", "status": "active", "is2FAEnabled": true, "is2FAEnrolled": true, "isDefaultPINChanged": true, "disposable": false, "refundAccount": null, "isDeleted": false, "createdAt": "2024-10-14T10:22:13.170Z", "updatedAt": "2024-11-26T03:33:55.778Z", "__v": 0 }, "amount": 0, "fee": 5, "vat": 0, "approved": false, "currency": "NGN", "status": "pending", "authorizationMethod": "chip", "balanceTransactions": [], "merchantAmount": 1000, "merchantCurrency": "NGN", "merchant": { "category": "7399", "name": "SUDO SIMULATOR", "merchantId": "SUDOSIMULATOR01", "city": "JAHI", "state": "AB", "country": "NG", "postalCode": "100001", "_id": "67af0942376903b183b034b5" }, "terminal": { "rrn": "142123123678", "stan": "102007", "terminalId": "2SUDOSIM", "terminalOperatingEnvironment": "on_premise", "terminalAttendance": "unattended", "terminalType": "pos", "panEntryMode": "magnetic_stripe", "pinEntryMode": "magnetic_stripe", "cardHolderPresence": true, "cardPresence": true, "_id": "67af0942376903b183b034b6" }, "transactionMetadata": { "channel": "pos", "type": "purchase", "reference": "8022418098142123123678", "_id": "67af0942376903b183b034b7" }, "pendingRequest": { "amount": 1005, "currency": "NGN", "merchantAmount": 1000, "merchantCurrency": "NGN", "_id": "67af0942376903b183b034b8" }, "requestHistory": [], "verification": { "billingAddressLine1": "not_provided", "billingAddressPostalCode": "not_provided", "cvv": "match", "expiry": "match", "pin": "match", "threeDSecure": "not_provided", "safeToken": "match", "authentication": "pin", "_id": "67af0942376903b183b034b9" }, "isDeleted": false, "createdAt": "2025-02-14T09:13:38.093Z", "updatedAt": "2025-02-14T09:13:38.093Z", "feeDetails": [{ "contract": "61a18b8a4ddab599d20344a7", "currency": "NGN", "amount": 5, "description": "Verve Card Authorization Fee", "_id": "67af0942376903b183b034ba" }], "__v": 0 }, "_id": "67af0942376903b183b034cd" }, "type": "authorization.request", "pendingWebhook": false, "webhookArchived": false, "createdAt": 1739524418, "_id": "67af0942376903b183b034cc" } ``` # Card Termination Source: https://docs.sudo.africa/docs/webhooks/card-termination This is a webhook triggered for when a card is terminated. The event type is `card.terminated` ```json json theme={null} { "type": "card.terminated", "environment": "production", "business": "xxxxxxxxxxxxx", "data": { "_id": "63edff25e63a129196e34c62", "business": "xxxxxxxxxxxxx", "customer": "xxxxxxxxxxxxx", "account": "xxxxxxxxxxxxx", "fundingSource": "xxxxxxxxxxxxx", "type": "virtual", "brand": "MasterCard", "currency": "USD", "maskedPan": "519075*******3531", "last4": "3531", "expiryMonth": "11", "expiryYear": "2027", "status": "canceled", "balance": 12.34 } } ``` # Failed Transaction Source: https://docs.sudo.africa/docs/webhooks/failed-transaction This webhook request is triggered whenever an attempt to charge a card fails. The event type is `authorization.decline` ## Request Payload ```json json theme={null} { "type": "authorization.declined", "environment": "production", "business": "xxxxxxxxxxxxx", "_id": "xxxxxxxxxxxxx", "data": { "object": { "business": "xxxxxxxxxxxxx", "customer": "xxxxxxxxxxxxx", "account": "xxxxxxxxxxxxx", "card": "xxxxxxxxxxxxx", "amount": -25.49, "fee": 0, "vat": 0, "approved": false, "currency": "USD", "status": "pending", "authorizationMethod": "online", "balanceTransactions": [], "merchantAmount": -25.49, "merchantCurrency": "USD", "merchant": {}, "terminal": {}, "transactionMetadata": {}, "pendingRequest": null, "requestHistory": [{ "amount": 25.49, "currency": "USD", "approved": false, "merchantAmount": 25.49, "merchantCurrency": "USD", "reason": "not_allowed", "narration": "No sufficient funds", "createdAt": "2024-05-20T13:40:25.079Z", "_id": "664b52c9c05d4a5d3f8cc734" }], "verification": {}, "isDeleted": false, "createdAt": "2024-05-20T13:40:25.079Z", "updatedAt": "2024-05-20T13:40:25.079Z", "_id": "664b52c9c05d4a5d3f8cc730", "feeDetails": [], "__v": 0 }, "_id": "664b52c9c05d4a5d3f8cc740" } } ``` # Successful Transaction Source: https://docs.sudo.africa/docs/webhooks/transaction-created This webhook request is triggered whenever a successful transaction happened on a card. The event type is `transaction.created` ## Request Payload ```json json theme={null} { "environment": "production", "business": "xxxxxxxxxxxxx", "data": { "object": { "business": "xxxxxxxxxxxxx", "customer": "xxxxxxxxxxxxx", "account": "xxxxxxxxxxxxx", "card": "xxxxxxxxxxxxx", "authorization": null, "amount": -0.15, "fee": 0, "vat": 0, "feeDetails": [], "currency": "USD", "type": "capture", "balanceTransactions": [], "merchantAmount": -0.15, "merchantCurrency": "USD", "merchant": { "category": "5399", "name": "Paystack", "merchantId": "-", "city": "Ikeja GRA ", "state": "-", "country": "NG", "postalCode": "-", "_id": "664b4cd13fc1976f98ce6cd0" }, "terminal": { "rrn": "88081577-3e50-4567-a990-712d31fd1759", "stan": "-", "terminalId": "-", "terminalOperatingEnvironment": "off_premise", "terminalAttendance": "unattended", "terminalType": "ecommerce", "panEntryMode": "keyed_in", "pinEntryMode": "keyed_in", "cardHolderPresence": false, "cardPresence": false, "_id": "664b4cd13fc1976f98ce6cd1" }, "transactionMetadata": { "channel": "web", "type": "purchase", "reference": "88081577-3e50-4567-a990-712d31fd1759", "_id": "664b4cd13fc1976f98ce6cd2" }, "isDeleted": false, "createdAt": "2024-05-20T13:14:57.730Z", "updatedAt": "2024-05-20T13:14:57.730Z", "_id": "664b4cd13fc1976f98ce6ccf", "__v": 0 }, "_id": "664b4cd13fc1976f98ce6cd6" }, "type": "transaction.created", "pendingWebhook": true, "webhookArchived": false, "createdAt": 1716210897, "_id": "xxxxxxxxxxxxx", "__v": 0 } ``` # Transaction Refund Source: https://docs.sudo.africa/docs/webhooks/transaction-refund This webhook request is triggered whenever a card was charged successful, but the transaction failed at a point or after chargeback dispute. The event type is `transaction.refund` ## Request Payload ```json json theme={null} { "environment": "production", "business": "xxxxxxxxxxxxxxxxxxxxx", "data": { "object": { "_id": "xxxxxxxxxxxxxxxxxxxxx", "business": "xxxxxxxxxxxxxxxxxxxxx", "customer": "xxxxxxxxxxxxxxxxxxxxx", "account": "xxxxxxxxxxxxxxxxxxxxx", "card": "xxxxxxxxxxxxxxxxxxxxx", "authorization": "xxxxxxxxxxxxxxxxxxxxx", "amount": 20005, "fee": 5, "vat": 0, "feeDetails": [{ "contract": "61a18b8a4ddab599d20344a7", "currency": "NGN", "amount": 5, "description": "Verve Card Authorization Fee", "_id": "66d39a9a74ffd5590104310d" }], "currency": "NGN", "type": "refund", "balanceTransactions": [], "merchantAmount": 20000, "merchantCurrency": "NGN", "merchant": { "category": "6014", "name": "T Jeezy Communicati 017", "merchantId": "2TEPLA000000002", "city": "225 2TEP6ZJD", "state": "LA", "country": "NG", "postalCode": "100001", "_id": "66d39a9274ffd559010430a4" }, "terminal": { "rrn": "000000017225", "stan": "017225", "terminalId": "2TEP6ZJD", "terminalOperatingEnvironment": "on_premise", "terminalAttendance": "attended", "terminalType": "pos", "panEntryMode": "magnetic_stripe", "pinEntryMode": "magnetic_stripe", "cardHolderPresence": true, "cardPresence": true, "_id": "66d39a9274ffd559010430a5" }, "transactionMetadata": { "channel": "pos", "type": "payment", "reference": "9160060154000000017225", "_id": "66d39a9274ffd559010430a6" }, "isDeleted": false, "createdAt": "2024-09-02T20:17:55.766Z", "updatedAt": "2024-09-02T20:17:55.766Z", "__v": 0 } }, "_id": "xxxxxxxxxxxxxxxxxxxxx", "type": "transaction.refund", "pendingWebhook": true, "webhookArchived": false, "createdAt": 1716210897, "__v": 0 } ``` # Overview Source: https://docs.sudo.africa/reference/accounts Each card issued has a corresponding account that hold it's funds. Naira accounts can receive funds from other banks via NIP. **Endpoints** | Method | Url | | ------ | ------------------------------------------------------------------ | | `POST` | [/accounts](/reference/create-account) | | `GET` | [/accounts](/reference/get-accounts) | | `GET` | [/accounts/:id](/reference/get-account) | | `GET` | [/accounts/:id/balance](/reference/get-account-balance) | | `GET` | [/accounts/:id/transactions](/reference/get-account-transactions) | | `GET` | [/accounts/banks](/reference/banks-list) | | `POST` | [/accounts/transfer/name-enquiry](/reference/name-enquiry) | | `POST` | [/accounts/transfer](/reference/fund-transfer) | | `GET` | [/accounts/transfers/:id](/reference/get-transfer-status) | | `GET` | [/accounts/transfers/rate/:currencyPair](/reference/transfer-rate) | **Account** **Object** ```json RESPONSE (STANDARD) theme={null} { "_id": "61d48b0000dfcdd1184ef93d", "business": "61d4140e00c7cdd1184ef455", "type": "account", "currency": "USD", "accountName": "Sudo Settlement Account", "accountType": "Current", "currentBalance": 540, "availableBalance": 540, "provider": "Sudo", "providerReference": "acc_1641319168742", "referenceCode": "acc_1641319168742", "isDefault": true, } ``` **Account** **Transaction** **Object** ```json RESPONSE (STANDARD) theme={null} { "_id": "61d48b0e00c7cdd1184ef9e2", "business": "61d4150e30c7cdd1184ef455", "customer": null, "account": { "_id": "61d48b0000c7cdd1184ef93d", "business": "61d4150b00c7cdd1184ef455", "type": "account", "currency": "USD", "accountName": "Sudo Settlement Account", "accountType": "Current", "currentBalance": 540, "availableBalance": 540, "provider": "Sudo", "providerReference": "acc_1641319168742", "referenceCode": "acc_1641319168742", "isDefault": true, "isDeleted": false, "createdAt": "2022-01-04T17:59:28.742Z", "updatedAt": "2022-01-04T17:59:28.742Z", "__v": 0 }, "paymentReference": "FUND_ACC_1641319182915", "type": "Credit", "provider": "Sudo", "providerChannel": "Internal", "amount": 540, "runningBalance": 540, "narration": "Sudo Simulator Account Funding", } ``` # Overview Source: https://docs.sudo.africa/reference/authorizations An authorization object is created when a card is used either at an ATM, POS or Online. **Endpoints** | Method | Url | | ------ | --------------------------------------------------------------- | | `GET` | [/cards/authorizations](/reference/get-authorizations) | | `GET` | [/cards/:id/authorizations](/reference/get-card-authorizations) | | `GET` | [/cards/authorizations/:id](/reference/get-authorization) | | `PUT` | [/cards/authorizations/:id](/reference/update-authorization) | **Authorization** **Object** ```json RESPONSE (STANDARD) theme={null} { "_id": "61c85ea290355e0de7c78974", "type": "authorization.request", "pendingWebhook": false, "webhookArchived": false, "environment": "development", "business": "61a1cc1abe4cea7fb487eba3", "data": { "amount": 10000, "fee": 5, "vat": 0.375, "approved": false, "currency": "NGN", "status": "pending", "authorizationMethod": "chip", "merchantAmount": 2, "merchantCurrency": "NGN", "customer": { ... }, "card": { ... }, "account": { ... }, "merchant": { ... }, "terminal": { ... }, "transactionMetadata": { ... }, "pendingRequest": { ... }, "verification": { ... }, "feeDetails": { ... } } } ``` # Balance Enquiry Source: https://docs.sudo.africa/reference/balance-enquiry openapi.json post /cards/simulator/balance_enqiry Simulates card enquiry for a specific card. # Digitalize Card Source: https://docs.sudo.africa/reference/card-digitalization openapi.json put /cards/digitalize/{id} Fetches required payload for Card Digitalization SDK for a specific card. # Overview Source: https://docs.sudo.africa/reference/card-programs A Card Program is a centralized configuration that governs how a batch or category of cards is issued, funded, and controlled. **Endpoints** | Method | Url | | ------ | ------------------------------------------------------------- | | `POST` | [/card-programs](/reference/create-card-program) | | `GET` | [/card-programs](/reference/get-card-programs) | | `GET` | [/card-programs/:id](/reference/get-card-program) | | `GET` | [/card-programs/:id/cards](/reference/get-card-program-cards) | | `PUT` | [/card-programs/:id](/reference/update-card-program) | # Overview Source: https://docs.sudo.africa/reference/cards A card is a payment instrument that enables users to conduct transactions at merchant locations. **Endpoints** | Method | Url | | ------ | ------------------------------------------------------ | | `POST` | [/cards](/reference/create-card) | | `GET` | [/cards](/reference/get-cards) | | `GET` | [/cards/customer/:id](/reference/get-customer-cards) | | `GET` | [/card/:id](/reference/get-card) | | `GET` | [/card/:id/balance](/reference/get-card-balance) | | `PUT` | [/card/:id/send-pin](/reference/send-default-card-pin) | | `PUT` | [/card/:id/pin](/reference/change-card-pin) | | `PUT` | [/card/:id/enroll2fa](/reference/enroll-card-for-2fa) | | `PUT` | [/card/:id](/reference/update-card) | | `GET` | [/card/digitalize/:id](/reference/card-digitalization) | | `GET` | [/card/:id/token](/reference/generate-card-token) | | `POST` | [/card/order](/reference/order-cards) | **Card** **Object** ```json RESPONSE (STANDARD) theme={null} { "_id": "61d7004261f2a5ebf1e18a63", "business": "61d4150b00c7cdd1184ef455", "customer": { "_id": "61d6baf144b3a74b6b258630", "business": "61d57b7ebc8deefb4330f5b4", "type": "individual", "name": "Farouk Bakre", "status": "active", "individual": { "firstName": "Farouk", "lastName": "Bakre", "identity": { "type": "BVN", "number": "23456543", "_id": "61d6baf144b3a74b6b258632" }, "_id": "61d6baf144b3a74b6b258631" }, "billingAddress": { "line1": "Ikeja, Lagos", "line2": "", "city": "Lagos", "state": "Lagos", "country": "Nigeria", "postalCode": "100001", "_id": "61d6baf144b3a74b6b258633" }, }, "account": { "_id": "61d7004261f2a5ebf1e18a61", "business": "61d4150b00c7cdd1184ef455", "type": "wallet", "currency": "NGN", "accountName": "SUDO / FAROUK BAKRE", "bankCode": "999240", "accountType": "Current", "accountNumber": "8016813168", "currentBalance": 0, "availableBalance": 0, "provider": "SafeHaven", "providerReference": "61d70040240846001ebeb8ff", "referenceCode": "subacc_1641480256224", "isDefault": true, }, "fundingSource": { "_id": "61d4150b00c7cdd1184ef459", "business": "61d4150b00c7cdd1184ef455", "type": "default", "status": "active", "jitGateway": null, "isDefault": true, }, "type": "physical", "brand": "Verve", "currency": "NGN", "maskedPan": "506100******2989", "expiryMonth": "01", "expiryYear": "2025", "status": "active", "spendingControls": { "channels": { "atm": true, "pos": true, "web": true, "mobile": true, "_id": "61d7004261f2a5ebf1e18a65" }, "allowedCategories": [], "blockedCategories": [], "spendingLimits": [ { "amount": 10000000, "interval": "daily", "categories": [], "_id": "61d7004261f2a5ebf1e18a66" } ], "_id": "61d7004261f2a5ebf1e18a64" }, } ``` # Change Card PIN Source: https://docs.sudo.africa/reference/change-card-pin openapi.json put /cards/{id}/pin Change PIN for a specific card. Available for both Verve and AfriGo (physical and virtual) cards. # Create Account Source: https://docs.sudo.africa/reference/create-account openapi.json post /accounts Creates a new account. # Create Card Source: https://docs.sudo.africa/reference/create-card openapi.json post /cards Create, map or replace cards for a specific customer. # Create Card Program Source: https://docs.sudo.africa/reference/create-card-program openapi.json post /card-programs # Create Customer Source: https://docs.sudo.africa/reference/create-customer openapi.json post /customers Creates a new customer. # Create Dispute Source: https://docs.sudo.africa/reference/create-dispute openapi.json post /cards/disputes Creates a dispute for a specific transaction. # Create Funding Source Source: https://docs.sudo.africa/reference/create-funding-source openapi.json post /fundingsources Creates a new funding source. # Overview Source: https://docs.sudo.africa/reference/customers A customer is either an individual or business entity that can be issued a payment card. **Endpoints** | Method | Url | | ------ | ------------------------------------------------------------------------- | | `POST` | [/customers](/reference/create-customer) | | `GET` | [/customers](/reference/get-customers) | | `GET` | [/customers/:id](/reference/get-customer) | | `PUT` | [/customers/:id](/reference/update-customer) | | `PUT` | [/customers/:id/documents/url](/reference/generate-customer-document-url) | **Customer** **Object** ```json RESPONSE (STANDARD) theme={null} { "_id": "61d4311000c7cdd1184ef76a", "business": "61d4150b00c7cdd1384f2355", "type": "individual", "name": "John Doe", "status": "active", "individual": { "firstName": "John", "lastName": "Doe", "_id": "61dc10951160e92757b1c4a1" }, "billingAddress": { "line1": "Ikeja, Lagos", "line2": "", "city": "Lagos", "state": "Lagos", "country": "Nigeria", "postalCode": "100001", "_id": "61d4311000c7cdd1184ef76d" }, "emailAddress": "[email protected]", "phoneNumber": "+234 8145431688", "metadata": { "name": "john" } } ``` # Overview Source: https://docs.sudo.africa/reference/disputes You can dispute transactions that the cardholder does not recognize, suspects to be fraudulent, or has other issues with. **Endpoints** | Method | Url | | ------ | ------------------------------------------------ | | `POST` | [/cards/disputes](/reference/create-dispute) | | `GET` | [/cards/disputes](/reference/get-disputes) | | `GET` | [/cards/disputes/:id](/reference/get-dispute) | | `PUT` | [/cards/disputes/:id](/reference/update-dispute) | **Dispute** **Object** ```json RESPONSE (STANDARD) theme={null} { "business": "61d4150b00c7cdd1184ef455", "transaction": "61d7e0ce67ccf96ec7f893c0", "balanceTransactions": [], "amount": -110.75, "currency": "NGN", "status": "submitted", "reason": "not_received", "explanation": "This is the explanation", "_id": "61de8ee57dcd42f2dff12f71", } ``` # Enroll Card for 2FA Source: https://docs.sudo.africa/reference/enroll-card-for-2fa openapi.json put /cards/{id}/enroll2fa Enroll 2FA for a specific card. Only available for Verve and AfriGo cards at the moment. # Fund Account Source: https://docs.sudo.africa/reference/fund-account openapi.json post /accounts/simulator/fund Funds an account with the specified amount. # Fund Transfer Source: https://docs.sudo.africa/reference/fund-transfer openapi.json post /accounts/transfer Transfers funds from one account, wallet, or card to another. # Overview Source: https://docs.sudo.africa/reference/funding-sources A funding source represents a bank account from which funds are drawn for authorizations. **Endpoints** | Method | Url | | ------ | ------------------------------------------------------- | | `POST` | [/fundingsources](/reference/create-funding-source) | | `GET` | [/fundingsources](/reference/get-funding-sources) | | `GET` | [/fundingsources/:id](/reference/get-funding-source) | | `PUT` | [/fundingsources/:id](/reference/update-funding-source) | **Funding** **Source** **Object** ```json RESPONSE (STANDARD) theme={null} { "_id": "61d4150b01c6cdd1184ef459", "business": "61d4150b1dc7cdd1184ef455", "type": "default", "status": "active", "jitGateway": null, "isDefault": true, } ``` # Generate Card Token Source: https://docs.sudo.africa/reference/generate-card-token openapi.json get /cards/{id}/token This endpoint generates a card token to be used for displaying sensitive card data. # Generate Customer Document URL Source: https://docs.sudo.africa/reference/generate-customer-document-url openapi.json put /customers/{id}/documents/url Generate a document upload URL for a specific customer KYC. # Generate Test Card Source: https://docs.sudo.africa/reference/generate-test-card openapi.json get /cards/simulator/generate Generates a test card which can be mapped to a customer or used to simulate authorisations or balance enquiry. # Get Account Source: https://docs.sudo.africa/reference/get-account openapi.json get /accounts/{id} Fetches a specific account object. Provide the account `_id` and the corresponding object will be returned. # Get Account Balance Source: https://docs.sudo.africa/reference/get-account-balance openapi.json get /accounts/{id}/balance Fetches the balance of the specified account. # Get Account Transactions Source: https://docs.sudo.africa/reference/get-account-transactions openapi.json get /accounts/{id}/transactions Fetches all transactions of the specified account according to the query parameters. # Get Accounts Source: https://docs.sudo.africa/reference/get-accounts openapi.json get /accounts Fetches existing accounts according to query parameters. # Get Authorization Source: https://docs.sudo.africa/reference/get-authorization openapi.json get /cards/authorizations/{id} Fetches details about a specific authorization using the provided id. # Get Authorizations Source: https://docs.sudo.africa/reference/get-authorizations openapi.json get /cards/authorizations Fetches all authorizations for all cards according to the query parameters. # Get Card Source: https://docs.sudo.africa/reference/get-card openapi.json get /cards/{id} Fetches the details of a specific card. # Get Card Authorizations Source: https://docs.sudo.africa/reference/get-card-authorizations openapi.json get /cards/{id}/authorizations Fetches all authorizations for a specific card according to the query parameters. # Get Card Balance Source: https://docs.sudo.africa/reference/get-card-balance openapi.json get /cards/{id}/balance Fetches the balance of the specified card. # Get a Card Program Source: https://docs.sudo.africa/reference/get-card-program openapi.json get /card-programs/{id} # Get Cards By a Program Source: https://docs.sudo.africa/reference/get-card-program-cards openapi.json get /card-programs/{id}/cards # Get Card Programs Source: https://docs.sudo.africa/reference/get-card-programs openapi.json get /card-programs # Get Card Transactions Source: https://docs.sudo.africa/reference/get-card-transactions openapi.json get /cards/{id}/transactions Fetches all transactions for a specific card according to the query parameters. # Get Cards Source: https://docs.sudo.africa/reference/get-cards openapi.json get /cards Fetches cards according to query parameters. # Get Customer Source: https://docs.sudo.africa/reference/get-customer openapi.json get /customers/{id} Fetches a specific customer object. Provide the customer `_id` and the corresponding object will be returned. # Get Customer Cards Source: https://docs.sudo.africa/reference/get-customer-cards openapi.json get /cards/customer/{id} Fetches all cards that are mapped to a specific customer according to the query parameters. # Get Customers Source: https://docs.sudo.africa/reference/get-customers openapi.json get /customers Fetches existing customer objects according to query parameters. # Get Dispute Source: https://docs.sudo.africa/reference/get-dispute openapi.json get /cards/disputes/{id} Fetches details about a specific dispute using the provided id. # Get Disputes Source: https://docs.sudo.africa/reference/get-disputes openapi.json get /cards/disputes Fetches all disputes according to the query parameters. # Get Funding Source Source: https://docs.sudo.africa/reference/get-funding-source openapi.json get /fundingsources/{id} Fetches a specific funding source object. Provide the source `_id` and the corresponding object will be returned. # Get Funding Sources Source: https://docs.sudo.africa/reference/get-funding-sources openapi.json get /fundingsources Fetches existing funding sources. # Get Transaction Source: https://docs.sudo.africa/reference/get-transaction openapi.json get /cards/transactions/{id} Fetches details about a specific transaction using the provided id. # Get Transactions Source: https://docs.sudo.africa/reference/get-transactions openapi.json get /cards/transactions Fetches all transactions for all cards according to the query parameters. # Introduction Source: https://docs.sudo.africa/reference/introduction The Sudo API is organized around [**REST**](http://en.wikipedia.org/wiki/Representational_State_Transfer). Our API has predictable resource-oriented URLs, accepts JSON request bodies, returns [JSON-encoded](http://www.json.org/) responses, and uses standard HTTP response codes, authentication, and verbs. You can use the Sudo sandbox environment which does not interact with live banking networks while integrating our APIs. To set up a sandbox account, go to [https://app.sudo.africa](https://app.sudo.africa). **API** **Base** **Url** ```text LIVE ENVIRONMENT theme={null} https://api.sudo.africa ``` ```text SANDBOX ENVIRONMENT theme={null} https://api.sandbox.sudo.cards ``` **Authentication** Sudo API uses API keys to authenticate requests. You can view and manage your API keys from the [Sudo Dashboard](https://app.sudo.africa). Your API keys carry many privileges, so be sure to keep them secure! Do not share your secret API keys in publicly accessible areas such as GitHub, client-side code, and so forth. All API requests must be made over [HTTPS](http://en.wikipedia.org/wiki/HTTP_Secure). Calls made over plain HTTP will fail. API requests without authentication will also fail. **Errors** Sudo uses conventional HTTP response codes to indicate the success or failure of an API request. In general: Codes in the 2xx range indicate success. Codes in the 4xx range indicate an error that failed given the information provided (e.g., a required parameter was omitted, a charge failed, etc.). Codes in the 5xx range indicate an error with Sudo's servers (these are rare). | Code | Description | | ------------------------------------------ | ------------------------------------------------------------------------ | | `200` - OK | Everything worked as expected. | | `400` - Bad Request | The request was unacceptable, often due to missing a required parameter. | | `401` - Unauthorized | No valid API key provided. | | `402` - Request Failed | The parameters were valid but the request failed. | | `403` - Forbidden | The API key doesn't have permissions to perform the request. | | `404` - Not Found | The requested resource doesn't exist. | | `429` - Too Many Requests | Too many requests hit the API too quickly. | | `500`, `502`, `503`, `504` - Server Errors | Something went wrong on Sudo's end. (These are rare.) | **Metadata** The Sudo API allows you to store useful additional structured information on an object. You can store multiple key-value pairs which will be available on the data object at anytime when retrieved. Sudo does not make use of any data you store in the metadata object. Do not store any sensitive information (card details, passwords etc.) as metadata. **Pagination** Sudo supports fetch of all top-level API resources like Customers, Accounts, Cards, Authorizations, Transactions, etc. These endpoints share a common structure, taking at least these two parameters: `page` and `limit`. By default page is set to `0` and limit `25`. You can fetch a maximum of `100` records at once. The resulting response will always include a `pagination` object with the `total` records count, number of `pages`, the current `page` and `limit` set. ```json json theme={null} { "statusCode": 200, "message": "Cards fetched successfully.", "data": [ {.....}, {.....}, {.....} ], "pagination": { "total": 1, "pages": 1, "page": "0", "limit": "25" } } ``` # Order Cards Source: https://docs.sudo.africa/reference/order-cards openapi.json post /cards/order Order Physical Cards # Send Default Card PIN Source: https://docs.sudo.africa/reference/send-default-card-pin openapi.json put /cards/{id}/send-pin Send Default PIN for a specific card. Only available for Verve and AfriGo cards at the moment. # Simulate Authorization Source: https://docs.sudo.africa/reference/simulate-authorization openapi.json post /cards/simulator/authorization Simulates different types of authorizations across several merchant categories and channels. # Simulate Authorization Reversal Source: https://docs.sudo.africa/reference/simulate-authorization-reversal openapi.json get /cards/simulator/authorization/{id}/reversal Simulates different types of authorizations across several merchant categories and channels. # Overview Source: https://docs.sudo.africa/reference/simulators This set of endpoints can be used to perform several simulations on the sandbox environment. **Endpoints** | Method | Url | | ------ | ----------------------------------------------------------------------------------------- | | `POST` | [/accounts/simulator/fund](/reference/fund-account) | | `GET` | [/cards/simulator/generate](/reference/generate-test-card) | | `POST` | [/cards/simulator/balance\_enqiry](/reference/balance-enquiry) | | `POST` | [/cards/simulator/authorization](/reference/simulate-authorization) | | `GET` | [/cards/simulator/authorization/:id/reversal](/reference/simulate-authorization-reversal) | # Overview Source: https://docs.sudo.africa/reference/transactions Once an authorization request is approved, the status on the authorization is updated to pending, and the authorization.updated webhook event is sent. The amount is deducted from your default available balance. A transaction is then created and the status of the authorization is set to closed. **Endpoints** | Method | Url | | ------ | ----------------------------------------------------------- | | `GET` | [/cards/transactions](/reference/get-transactions) | | `GET` | [/cards/:id/transactions](/reference/get-card-transactions) | | `GET` | [/cards/transactions/:id](/reference/get-transaction) | | `PUT` | [/cards/transactions/:id](/reference/update-transaction) | **Transaction** **Object** ```json RESPONSE (STANDARD) theme={null} { "_id": "61d7e0ce67ccf96ec7f893c0", "business": "61d4150b00c7cdd1184ef455", "customer": { "_id": "61d6baf144b3a74b6b258630", "business": "61d57b7ebc8deefb4330f5b4", "type": "individual", "name": "John Doe", "status": "active", "individual": { "firstName": "John", "lastName": "Doe", "identity": { "type": "BVN", "number": "2343456543", "_id": "61d6baf144b3a74b6b258632" }, "_id": "61d6baf144b3a74b6b258631" }, "billingAddress": { "line1": "Ikeja, Lagos", "line2": "", "city": "Lagos", "state": "Lagos", "country": "Nigeria", "postalCode": "100001", "_id": "61d6baf144b3a74b6b258633" }, }, "account": { "_id": "61d7004261f2a5ebf1e18a61", "business": "61d4150b00c7cdd1184ef455", "type": "wallet", "currency": "NGN", "accountName": "SUDO / JOHN DOE", "bankCode": "999240", "accountType": "Current", "accountNumber": "8016813168", "currentBalance": 0, "availableBalance": 0, "provider": "SafeHaven", "providerReference": "61d70040240846001ebeb8ff", "referenceCode": "subacc_1641480256224", "isDefault": true, }, "card": { "_id": "61d7004261f2a5ebf1e18a63", "business": "61d4150b00c7cdd1184ef455", "customer": "61d6baf144b3a74b6b258630", "account": "61d7004261f2a5ebf1e18a61", "fundingSource": "61d4150b00c7cdd1184ef459", "type": "physical", "brand": "Verve", "currency": "NGN", "maskedPan": "506100******2989", "expiryMonth": "01", "expiryYear": "2025", "status": "active", "spendingControls": { "channels": { "atm": true, "pos": true, "web": true, "mobile": true, "_id": "61d7004261f2a5ebf1e18a65" }, "allowedCategories": [], "blockedCategories": [], "spendingLimits": [ { "amount": 10000000, "interval": "daily", "categories": [], "_id": "61d7004261f2a5ebf1e18a66" } ], "_id": "61d7004261f2a5ebf1e18a64" }, }, "authorization": { "_id": "61d7e0cd67ccf96ec7f893a4", "business": "61d4150b00c7cdd1184ef455", "customer": "61d6baf144b3a74b6b258630", "account": "61d7004261f2a5ebf1e18a61", "card": "61d7004261f2a5ebf1e18a63", "amount": 105.375, "fee": 5, "vat": 0.375, "approved": true, "currency": "NGN", "status": "approved", "authorizationMethod": "chip", "balanceTransactions": [], "merchantAmount": 100, "merchantCurrency": "NGN", "merchant": { "category": "3057", "name": "SUDO SIMULATOR", "merchantId": "SUDOSIMULATOR01", "city": "JAHI", "state": "AB", "country": "UJ", "postalCode": "100001", "_id": "61d7e0cd67ccf96ec7f893a5" }, "terminal": { "rrn": "102910479385", "stan": "111184", "terminalId": "1SUDOSIM", "terminalOperatingEnvironment": "on_premise", "terminalAttendance": "unattended", "terminalType": "adminstrative_terminal", "panEntryMode": "magnetic_stripe", "pinEntryMode": "magnetic_stripe", "cardHolderPresence": true, "cardPresence": true, "_id": "61d7e0cd67ccf96ec7f893a6" }, "transactionMetadata": { "channel": "atm", "type": "cash_withdrawal", "reference": "1818113774102910479385", "_id": "61d7e0cd67ccf96ec7f893a7" }, "pendingRequest": null, "requestHistory": [ { "amount": 105.375, "currency": "NGN", "approved": true, "merchantAmount": 100, "merchantCurrency": "NGN", "reason": "card_active", "createdAt": "2022-01-07T06:42:21.663Z", "_id": "61d7e0ce67ccf96ec7f893c5" } ], "verification": { "billingAddressLine1": "not_provided", "billingAddressPostalCode": "not_provided", "cvv": "match", "expiry": "match", "pin": "match", "threeDSecure": "not_provided", "safeToken": "not_provided", "authentication": "pin", "_id": "61d7e0cd67ccf96ec7f893a9" }, "isDeleted": false, "createdAt": "2022-01-07T06:42:21.663Z", "updatedAt": "2022-01-07T06:42:21.663Z", "feeDetails": [ { "contract": "61a18b8a4ddab599d20344a7", "currency": "NGN", "amount": 5, "description": "Verve Card Authorization Fee", "_id": "61d7e0cd67ccf96ec7f893aa" } ], "__v": 1 }, "amount": -105.375, "fee": -5, "vat": -0.375, "currency": "NGN", "type": "capture", "balanceTransactions": [], "merchantAmount": -100, "merchantCurrency": "NGN", "merchant": { "category": "3057", "name": "SUDO SIMULATOR", "merchantId": "SUDOSIMULATOR01", "city": "JAHI", "state": "AB", "country": "UJ", "postalCode": "100001", "_id": "61d7e0cd67ccf96ec7f893a5" }, "terminal": { "rrn": "102910479385", "stan": "111184", "terminalId": "1SUDOSIM", "terminalOperatingEnvironment": "on_premise", "terminalAttendance": "unattended", "terminalType": "adminstrative_terminal", "panEntryMode": "magnetic_stripe", "pinEntryMode": "magnetic_stripe", "cardHolderPresence": true, "cardPresence": true, "_id": "61d7e0cd67ccf96ec7f893a6" }, "transactionMetadata": { "channel": "atm", "type": "cash_withdrawal", "reference": "1818113774102910479385", "_id": "61d7e0cd67ccf96ec7f893a7" }, "feeDetails": [ { "contract": "61a18b8a4ddab599d20344a7", "currency": "NGN", "amount": 5, "description": "Verve Card Authorization Fee", "_id": "61d7e0ce67ccf96ec7f893c4" } ], } ``` # Transfer Rate Source: https://docs.sudo.africa/reference/transfer-rate openapi.json get /accounts/transfer/rate/{currencyPair} Returns the exchange rate between two currency pairs. Currently supports `USDNGN` only. # Update Authorization Source: https://docs.sudo.africa/reference/update-authorization openapi.json put /cards/authorizations/{id} Fetches details about a specific authorization using the provided id. # Update Card Source: https://docs.sudo.africa/reference/update-card openapi.json put /cards/{id} Update details for a specific card. # Update a Card Program Source: https://docs.sudo.africa/reference/update-card-program openapi.json patch /card-programs/{id} # Update Customer Source: https://docs.sudo.africa/reference/update-customer openapi.json put /customers/{id} Updates the specific customer by setting the values of the parameters provided. Only provided parameters will be changed. # Update Dispute Source: https://docs.sudo.africa/reference/update-dispute openapi.json put /cards/disputes/{id} Updates a dispute for a specific transaction. # Update Funding Source Source: https://docs.sudo.africa/reference/update-funding-source openapi.json put /fundingsources/{id} Updates the specific funding source by setting the values of the parameters provided. # Update Transaction Source: https://docs.sudo.africa/reference/update-transaction openapi.json put /cards/transactions/{id} Fetches details about a specific transaction using the provided id.