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
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Wallet user UUID |
mobile | string | No | Saudi mobile number |
firstName | string | No | First name |
lastName | string | No | Last name |
email | string | No | Email address |
nationalId | string | No | Saudi national ID |
fullNameAr | string | No | Full name in Arabic from Nafath |
dateOfBirth | string | No | ISO 8601 date |
identityVerifiedAt | string | No | ISO 8601 timestamp of Nafath verification |
OnboardingOptionsView model
| Field | Type | Required | Description |
|---|---|---|---|
mobile | string | Yes | Saudi mobile number |
name | string | Yes | Full name. First word becomes first name; remaining words become last name |
email | string | No | Email 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
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Wallet user UUID |
mobile | string | No | Saudi mobile number |
firstName | string | No | First name |
lastName | string | No | Last name |
email | string | No | Email address |
nationalId | string | No | Saudi national ID |
fullNameAr | string | No | Full name in Arabic from Nafath |
dateOfBirth | string | No | ISO 8601 date |
identityVerifiedAt | string | No | ISO 8601 timestamp of Nafath verification |
LoginOptionsView model
| Field | Type | Required | Description |
|---|---|---|---|
mobile | string | Yes | Saudi 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
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Wallet user UUID |
mobile | string | No | Saudi mobile number |
firstName | string | No | First name |
lastName | string | No | Last name |
email | string | No | Email address |
nationalId | string | No | Saudi national ID |
fullNameAr | string | No | Full name in Arabic from Nafath |
dateOfBirth | string | No | ISO 8601 date |
identityVerifiedAt | string | No | ISO 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
| Field | Type | Required | Description |
|---|---|---|---|
totalBalance | string | Yes | Total wallet balance |
availableBalance | string | Yes | Spendable balance after holds |
holdBalance | string | Yes | Amount 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
| Field | Type | Required | Description |
|---|---|---|---|
entries | WalletTransaction[] | Yes | Page of transactions |
total | number | Yes | Total matching transactions |
page | number | Yes | Current 1-based page |
limit | number | Yes | Page size |
WalletTransactionView model
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Transaction UUID |
journalReference | string | Yes | Ledger journal reference |
transactionType | string | No | Transaction type label |
amount | string | Yes | Decimal amount |
direction | Debit or Credit | Yes | Money direction |
description | string | No | Human-readable note |
createdAt | string | No | ISO 8601 timestamp |
channel | string | No | CARD, SARIE, ONLINE, or absent |
PageOptionsView model
| Field | Type | Required | Description |
|---|---|---|---|
page | number | No | 1-based page number. Default: 1 |
limit | number | No | Page 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
| Field | Type | Required | Description |
|---|---|---|---|
topUpId | string | Yes | Top-up UUID |
status | string | Yes | Top-up status |
amount | string | Yes | Decimal amount loaded |
ncbReference | string | No | NCB bank reference when available |
balance | WalletBalance | Yes | Updated wallet balance |
WalletBalanceView model
| Field | Type | Required | Description |
|---|---|---|---|
totalBalance | string | Yes | Total wallet balance |
availableBalance | string | Yes | Spendable balance after holds |
holdBalance | string | Yes | Amount on hold |
Manage beneficiaries
Beneficiaries are saved transfer recipients.
- List existing recipients.
- Add a recipient using a mobile number.
- Ask for a verification code when required.
- Verify the recipient.
- 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
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Beneficiary UUID |
beneficiaryUserId | string | Yes | Recipient wallet user UUID |
mobile | string | Yes | Recipient mobile number |
displayName | string | No | Recipient profile name |
nickname | string | No | Custom label |
createdAt | string | No | ISO 8601 timestamp |
accountNumber | string | No | Wallet account number |
relation | string | No | Relationship label |
verificationRequired | boolean | No | Whether OTP verification is required |
verificationExpiresIn | number | No | Seconds until verification code expires |
AddBeneficiaryOptionsView model
| Field | Type | Required | Description |
|---|---|---|---|
mobile | string | Yes | Recipient mobile number |
nickname | string | No | Custom recipient label |
WalletTransactionsView model
| Field | Type | Required | Description |
|---|---|---|---|
entries | WalletTransaction[] | Yes | Page of transactions |
total | number | Yes | Total matching transactions |
page | number | Yes | Current 1-based page |
limit | number | Yes | Page 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
| Field | Type | Required | Description |
|---|---|---|---|
transferId | string | Yes | Transfer UUID |
status | string | Yes | Transfer status |
amount | string | Yes | Decimal amount transferred |
beneficiaryId | string | Yes | Recipient UUID |
balance | WalletBalance | Yes | Updated wallet balance |
TransferOptionsView model
| Field | Type | Required | Description |
|---|---|---|---|
beneficiaryId | string | Yes | Verified beneficiary UUID |
amount | string | Yes | Decimal amount |
note | string | No | Transfer note |
WalletBalanceView model
| Field | Type | Required | Description |
|---|---|---|---|
totalBalance | string | Yes | Total wallet balance |
availableBalance | string | Yes | Spendable balance after holds |
holdBalance | string | Yes | Amount 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
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Card UUID |
brand | string | Yes | Card network |
cardType | CREDIT or DEBIT | Yes | Card type |
tier | string | No | Card tier |
status | string | Yes | Card status |
cardholderName | string | No | Name printed on card |
last4 | string | Yes | Last four PAN digits |
expiryMonth | number | Yes | Month from 1 to 12 |
expiryYear | number | Yes | Four-digit year |
createdAt | string | No | ISO 8601 timestamp |
WalletCardDetailsView model
| Field | Type | Required | Description |
|---|---|---|---|
WalletCard fields | WalletCard | Yes | Card data except createdAt |
number | string | Yes | Full PAN |
cvv | string | Yes | Card CVV |
WalletCardSpendResultView model
| Field | Type | Required | Description |
|---|---|---|---|
spendId | string | Yes | Spend transaction UUID |
cardId | string | Yes | Card UUID used |
status | string | Yes | Spend status |
amount | string | Yes | Decimal amount spent |
balance | WalletBalance | Yes | Updated wallet balance |
IssueCardOptionsView model
| Field | Type | Required | Description |
|---|---|---|---|
cardType | CREDIT or DEBIT | Yes | Card type to issue |
tier | string | No | Card tier |
SpendOptionsView model
| Field | Type | Required | Description |
|---|---|---|---|
cardId | string | Yes | Card UUID |
amount | string | Yes | Decimal amount |
description | string | No | Spend 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:
APINETWORKDECODINGUNAUTHORIZEDINVALID_CONFIGURATIONINVALID_MOBILE_NUMBERCANCELLEDUSER_CANCELLEDNO_ACTIVE_SESSIONUNSUPPORTED_PLATFORMINVALID_ARGUMENTUNKNOWN
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
| Field | Type | Required | Description |
|---|---|---|---|
code | WalletSDKErrorCode | Yes | Stable machine-readable code |
message | string | Yes | Localized human-readable message |
messageAr | string | No | Arabic message from backend |
statusCode | number | No | HTTP status for API errors |
apiCode | string | No | Backend error code |