Mobile SDKs
One architecture, three packages, and a native payment sheet without a WebView.
The three mobile SDKs are the same design in three languages. Your server creates the payment and
hands the app an authorization_token. The app builds a session from that token, shows a native
sheet, and the payer never leaves your application.
There is no WebView anywhere in this path, which is the whole point. Mobile Money fields are native, card fields are Stripe's native components, and only a 3DS challenge or a bank redirect opens the system browser.
The three packages
| Platform | Package | Registry | Version |
|---|---|---|---|
| Flutter | wajub_mobile | pub.dev | 1.1.1 |
| React Native | @wajub/react-native | npm | 1.1.1 |
| Android | com.wajub:wajub-mobile-core | Maven Central | 1.1.0 |
| Android | com.wajub:wajub-mobile-compose | Maven Central | 1.1.0 |
There is no Swift package
Native iOS has no Wajub SDK. A Swift application reaches Wajub through the hosted checkout in a
SFSafariViewController, or through Flutter or React Native. Weighing that choice is on
Choose your SDK.
The flow, once
Every one of the three follows these five steps, and only the middle one is yours to design.
Your server Your app
─────────── ────────
POST /payments with sk.
returns authorization_token ──▶ createSession(token)
│
├─ loads the session and its channels
├─ shows the sheet you chose
└─ processes the payment natively
│
webhook payment.succeeded ◀──────────┘
fulfils the orderThe token is the only credential the app ever holds. It is scoped to one payment, it expires, and it cannot list, refund or read anything else on your account. Your secret key stays on your server.
What the session actually talks to
The SDK calls api.wajub.com/pay/* with Authorization: Bearer {token}. Those are the same
endpoints the hosted checkout page uses, which is why the mobile sheet behaves identically and
supports the same operators.
| Endpoint | What the SDK does with it |
|---|---|
GET /pay/session | The amount, the currency, the branding, and the channels available |
GET /pay/sdk-config | Per channel provider settings, including the Stripe publishable key |
POST /pay/process | Submits the payment on the chosen channel |
GET /pay/status | Polls the outcome when realtime is unavailable |
POST /pay/cancel | Abandons the session and returns a redirect URL |
Process is rate limited tighter than the rest
POST /pay/process allows ten attempts per five minutes per token and address. That is enough
for a payer retrying a wrong PIN and not enough to brute force one. Do not put a retry loop
around it.
Choosing the surface
Each SDK gives you two levels. The sheet is the fast path, the session methods are the one to reach for when the payment has to live inside your own design.
| You want | Use |
|---|---|
| A working payment today | The provided payment sheet |
| Your own screens, your own branding | The session methods, payMobileMoney and payCard |
| The amount on a screen before paying | loadSession(), then whatever you like |
| A live status while the payer confirms | watchStatus() |
Realtime, or polling
The session carries an echo block when the account has realtime enabled. When it is present the
SDK subscribes over Pusher and receives the outcome as it happens. When it is absent it falls back
to polling GET /pay/status every five seconds. Your code calls watchStatus() either way and
does not choose.
The webhook is still what fulfils the order
A Mobile Money confirmation can land after the payer has closed the app, and a phone can lose signal mid-approval. Use the sheet result to decide which screen to show and the webhook to release the goods.
Amounts and errors are shared too
Amounts are in the major unit everywhere, so 25000 with XAF is twenty-five thousand francs.
The error type is the same five-way union in all three SDKs.
| Error type | Meaning |
|---|---|
authenticationError | The token is expired, used or unknown |
invalidRequestError | A field the channel refused, param names it |
paymentError | The provider declined, declineCode says why |
rateLimitError | Too many attempts, retryAfterSeconds carries the wait |
apiError | Wajub side, or the network |
Every error carries retryable, which is what decides whether to offer the payer another go.
Pick your platform
Related pages
Related pages
- Create a paymentThe server call that produces the token.
- Sessions & securityWhat a session token may and may not do.
- WebhooksThe confirmation that ships the order.
- Payment methodsEvery operator and channel slug.
- Test scenariosNumbers and cards that produce each outcome.
- Choose your SDKRuntime floors and the constraints that rule one out.