Wallet SDK

NearPay Wallet SDK adds wallet login, onboarding, balance, transactions, beneficiaries, transfers, top-up, and cards to mobile apps.

React Native uses the native iOS and Android SDKs. Wallet logic, secure session storage, networking, and screens remain native.

Supported platforms

  • iOS 16+ with Swift and SwiftUI
  • Android 7.0+ (API 24) with Kotlin and Jetpack Compose
  • React Native 0.76+ with New Architecture enabled

Choose implementation style

Each wallet feature shows two options:

  • Headless method returns data and lets your app own the interface.
  • Built-in UI method opens a complete native screen managed by the SDK.

Install

Choose your platform.

Requirements

  • Android API 24+
  • compileSdk 36+
  • Java 17
  • Kotlin 2.0.21+
  • Host activity based on ComponentActivity

Install local artifact

A remote Maven repository is not available yet. Publish the SDK to local Maven:

cd wallet-sdk/android
./gradlew :walletsdk:publishToMavenLocal

Add the repository:

repositories {
  google()
  mavenCentral()
  mavenLocal()
}

Add the dependency:

dependencies {
  implementation 'io.nearpay:wallet-sdk:0.2.0'
}

Start onboarding

Onboarding handles account details, OTP, identity verification, address, and terms. Do not open another modal while this flow is visible.

name uses the first word as the first name and remaining words as the last name.

let wallet = try await WalletSDK.onboarding(
    mobile: "0501234567",
    name: "Ahmed Ali",
    email: "[email protected]"
)
WalletUserView model
FieldTypeRequiredDescription
idstringYesWallet user UUID
mobilestringNoSaudi mobile number
firstNamestringNoFirst name
lastNamestringNoLast name
emailstringNoEmail address
nationalIdstringNoSaudi national ID
fullNameArstringNoFull name in Arabic from Nafath
dateOfBirthstringNoISO 8601 date
identityVerifiedAtstringNoISO 8601 timestamp of Nafath verification
OnboardingOptionsView model
FieldTypeRequiredDescription
mobilestringYesSaudi mobile number
namestringYesFull name. First word becomes first name; remaining words become last name
emailstringNoEmail address

Log in

Login opens native mobile-number and OTP screens. The call returns a wallet handle after success.

Keep the host screen visible until the call ends. Treat user dismissal as cancellation, not failure.

do {
    wallet = try await WalletSDK.login(mobile: "0501234567")
} catch WalletSDKError.userCancelled {
    return
}
WalletUserView model
FieldTypeRequiredDescription
idstringYesWallet user UUID
mobilestringNoSaudi mobile number
firstNamestringNoFirst name
lastNamestringNoLast name
emailstringNoEmail address
nationalIdstringNoSaudi national ID
fullNameArstringNoFull name in Arabic from Nafath
dateOfBirthstringNoISO 8601 date
identityVerifiedAtstringNoISO 8601 timestamp of Nafath verification
LoginOptionsView model
FieldTypeRequiredDescription
mobilestringYesSaudi mobile number

Get session

Restore during app startup. A successful restore returns a wallet handle without showing UI. Show your app's login entry point when no session exists.

do {
    wallet = try await WalletSDK.getSession()
} catch WalletSDKError.noActiveSession {
    wallet = nil
}
WalletUserView model
FieldTypeRequiredDescription
idstringYesWallet user UUID
mobilestringNoSaudi mobile number
firstNamestringNoFirst name
lastNamestringNoLast name
emailstringNoEmail address
nationalIdstringNoSaudi national ID
fullNameArstringNoFull name in Arabic from Nafath
dateOfBirthstringNoISO 8601 date
identityVerifiedAtstringNoISO 8601 timestamp of Nafath verification

Read balance

Balance contains total, available, and held amounts.

Built-in UI method

Opens the complete balance screen managed by the SDK.

await wallet.showBalance()

Headless method

Returns balance data so your app can render its own interface.

let balance = try await wallet.getBalance()
let available = balance.availableBalance
WalletBalanceView model
FieldTypeRequiredDescription
totalBalancestringYesTotal wallet balance
availableBalancestringYesSpendable balance after holds
holdBalancestringYesAmount on hold

Load transaction history

History is paginated. The first page is 1. The default page size is 20.

Built-in UI method

Opens the complete transaction history screen managed by the SDK.

await wallet.showTransactions()

Headless method

Returns transaction data so your app can render its own history screen.

let result = try await wallet.getTransactions(page: 1, limit: 20)

Each item contains amount, direction, reference, description, date, and an optional channel. Use direction to choose positive or negative display style.

WalletTransactionsView model
FieldTypeRequiredDescription
entriesWalletTransaction[]YesPage of transactions
totalnumberYesTotal matching transactions
pagenumberYesCurrent 1-based page
limitnumberYesPage size
WalletTransactionView model
FieldTypeRequiredDescription
idstringYesTransaction UUID
journalReferencestringYesLedger journal reference
transactionTypestringNoTransaction type label
amountstringYesDecimal amount
directionDebit or CreditYesMoney direction
descriptionstringNoHuman-readable note
createdAtstringNoISO 8601 timestamp
channelstringNoCARD, SARIE, ONLINE, or absent
PageOptionsView model
FieldTypeRequiredDescription
pagenumberNo1-based page number. Default: 1
limitnumberNoPage size. Default: 20; maximum: 1000

Top up wallet

Built-in UI method

Opens the complete top-up screen and flow managed by the SDK.

await wallet.showTopUp()

Headless method

Runs top-up without showing SDK screens. Use it when your app provides its own top-up interface. Require a positive amount with no more than two decimal places.

let result = try await wallet.topUp(
    amount: Decimal(string: "50.00")!
)

React Native amounts must remain decimal strings to prevent floating-point rounding.

TopUpResultView model
FieldTypeRequiredDescription
topUpIdstringYesTop-up UUID
statusstringYesTop-up status
amountstringYesDecimal amount loaded
ncbReferencestringNoNCB bank reference when available
balanceWalletBalanceYesUpdated wallet balance
WalletBalanceView model
FieldTypeRequiredDescription
totalBalancestringYesTotal wallet balance
availableBalancestringYesSpendable balance after holds
holdBalancestringYesAmount on hold

Manage beneficiaries

Beneficiaries are saved transfer recipients.

  1. List existing recipients.
  2. Add a recipient using a mobile number.
  3. Ask for a verification code when required.
  4. Verify the recipient.
  5. Enable transfers.

Built-in UI method

Opens the complete beneficiaries screen managed by the SDK.

await wallet.showBeneficiary()

Headless methods

Return beneficiary data so your app can manage its own recipient interface and flow.

val beneficiaries = wallet.listBeneficiaries()

val beneficiary = wallet.addBeneficiary(
  mobile = "0501234567",
  nickname = "Ahmed"
)

val verified = wallet.verifyBeneficiary(
  id = beneficiary.id,
  code = verificationCode
)

Use removeBeneficiary to remove a recipient. Use beneficiaryTransfers to load recipient history.

BeneficiaryView model
FieldTypeRequiredDescription
idstringYesBeneficiary UUID
beneficiaryUserIdstringYesRecipient wallet user UUID
mobilestringYesRecipient mobile number
displayNamestringNoRecipient profile name
nicknamestringNoCustom label
createdAtstringNoISO 8601 timestamp
accountNumberstringNoWallet account number
relationstringNoRelationship label
verificationRequiredbooleanNoWhether OTP verification is required
verificationExpiresInnumberNoSeconds until verification code expires
AddBeneficiaryOptionsView model
FieldTypeRequiredDescription
mobilestringYesRecipient mobile number
nicknamestringNoCustom recipient label
WalletTransactionsView model
FieldTypeRequiredDescription
entriesWalletTransaction[]YesPage of transactions
totalnumberYesTotal matching transactions
pagenumberYesCurrent 1-based page
limitnumberYesPage size

Transfer funds

Transfers require a verified beneficiary and valid amount. Disable the submit button while the request runs.

Built-in UI method

Opens the complete transfer screen and flow managed by the SDK.

await wallet.showTransfer()

Headless method

Runs the transfer without showing SDK screens. Use it when your app provides its own transfer interface.

let result = try await wallet.transfer(
    beneficiaryId: beneficiary.id,
    amount: Decimal(string: "25.00")!,
    note: "Lunch"
)

Refresh balance and transaction history after success.

TransferResultView model
FieldTypeRequiredDescription
transferIdstringYesTransfer UUID
statusstringYesTransfer status
amountstringYesDecimal amount transferred
beneficiaryIdstringYesRecipient UUID
balanceWalletBalanceYesUpdated wallet balance
TransferOptionsView model
FieldTypeRequiredDescription
beneficiaryIdstringYesVerified beneficiary UUID
amountstringYesDecimal amount
notestringNoTransfer note
WalletBalanceView model
FieldTypeRequiredDescription
totalBalancestringYesTotal wallet balance
availableBalancestringYesSpendable balance after holds
holdBalancestringYesAmount on hold

Manage cards

Card features include issue, list, details, and spend. Card types are CREDIT and DEBIT.

Built-in UI method

Opens the complete cards screen and flow managed by the SDK.

await wallet.showCards()

Each show call waits until the user closes the screen. Wait for any existing app modal to close before opening a wallet screen.

Headless methods

Return card data and results so your app can provide its own cards interface.

React Native

const card = await wallet.issueCard({ cardType: "DEBIT" });
const cards = await wallet.listCards();
const details = await wallet.getCardDetails(card.id);

const result = await wallet.spend({
  cardId: card.id,
  amount: "10.00",
  description: "Purchase",
});

iOS and Android provide the same issueCard, listCards, getCardDetails, and spend methods using native card kinds and decimal types.

WalletCardView model
FieldTypeRequiredDescription
idstringYesCard UUID
brandstringYesCard network
cardTypeCREDIT or DEBITYesCard type
tierstringNoCard tier
statusstringYesCard status
cardholderNamestringNoName printed on card
last4stringYesLast four PAN digits
expiryMonthnumberYesMonth from 1 to 12
expiryYearnumberYesFour-digit year
createdAtstringNoISO 8601 timestamp
WalletCardDetailsView model
FieldTypeRequiredDescription
WalletCard fieldsWalletCardYesCard data except createdAt
numberstringYesFull PAN
cvvstringYesCard CVV
WalletCardSpendResultView model
FieldTypeRequiredDescription
spendIdstringYesSpend transaction UUID
cardIdstringYesCard UUID used
statusstringYesSpend status
amountstringYesDecimal amount spent
balanceWalletBalanceYesUpdated wallet balance
IssueCardOptionsView model
FieldTypeRequiredDescription
cardTypeCREDIT or DEBITYesCard type to issue
tierstringNoCard tier
SpendOptionsView model
FieldTypeRequiredDescription
cardIdstringYesCard UUID
amountstringYesDecimal amount
descriptionstringNoSpend description

Card details contain PAN and CVV. Show them only when needed. Never log, cache, or persist them.

Handle session expiry

Register the handler during app startup. Clear the wallet reference and show the login state after the session expires.

WalletSDK.shared.onSessionExpired = {
    print("Session expired")
}

Sign out

Sign out clears the saved session and cached user. Clear your app's wallet state afterward.

  • iOS: call WalletSDK.signOut()
  • Android: call sdk.signOut()
  • React Native: await WalletSDK.signOut()

Error handling

Native SDKs return typed WalletSDKError values. React Native returns WalletSDKError with stable codes:

  • API
  • NETWORK
  • DECODING
  • UNAUTHORIZED
  • INVALID_CONFIGURATION
  • INVALID_MOBILE_NUMBER
  • CANCELLED
  • USER_CANCELLED
  • NO_ACTIVE_SESSION
  • UNSUPPORTED_PLATFORM
  • INVALID_ARGUMENT
  • UNKNOWN
try {
  await wallet.show("transactions");
} catch (error) {
  if (error instanceof WalletSDKError) {
    console.log(error.code, error.message);
  }
}

API errors can also include apiCode, statusCode, and messageAr.

WalletSDKErrorView model
FieldTypeRequiredDescription
codeWalletSDKErrorCodeYesStable machine-readable code
messagestringYesLocalized human-readable message
messageArstringNoArabic message from backend
statusCodenumberNoHTTP status for API errors
apiCodestringNoBackend error code