Mobile SDK

Drop the donation flow into your app without shipping a Wishbone secret.

@wishbone/sdk opens the hosted donation flow from inside your app (TypeScript / React Native). The cardholder never leaves your experience, and your binary never contains a Wishbone key.

Trust model

Your API key is a server secret. The SDK calls a tokenProvider that you implement; your backend mints a cardholder-scoped embed token via POST /v1/embed/token and returns it. The SDK opens the flow with that short-lived token.

Bank app ──(1) request flow──▶ Your backend ──(2) POST /v1/embed/token──▶ Wishbone
   ▲                                  │                                       │
   │                                  ◀──────────── embed token ──────────────┘
   └──(3) open hosted flow with the short-lived token (this SDK) ──▶ Wishbone

Embed tokens are encrypted, carry the cardholder and any pre-selection you set, and expire in 60–900 seconds (default 600).

Install

npm install @wishbone/sdk
# Recommended for the best in-app browser UX (pick one):
npm install expo-web-browser
# or
npm install react-native-inappbrowser-reborn

Usage

React Native
import { WishboneProvider, useWishbone } from "@wishbone/sdk/react";

function Root() {
  return (
    <WishboneProvider
      environment="production"
      tokenProvider={async (params) => {
        // Your backend holds the Wishbone API key.
        const res = await fetch("https://api.mybank.com/wishbone/embed-token", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify(params),
        });
        return res.json(); // { embedToken: "wbt_…" }
      }}
    >
      <App />
    </WishboneProvider>
  );
}

function DonateButton() {
  const { donate, loading } = useWishbone();
  return (
    <Button
      title="Donate points to Tennessee"
      disabled={loading}
      onPress={async () => {
        const result = await donate({
          schoolId: "sch_tennessee",
          fund: "annual_fund",
          amount: 50,
          pointsRedeemed: 5000,
          returnUrl: "mybank://wishbone/complete",
        });
        if (result.status === "completed") {
          // result.donationId, result.dollarAmount, …
        }
      }}
    />
  );
}

Result

donate() resolves with status of completed, pending, failed or cancelled, plus donationId, schoolId, dollarAmount, pointsRedeemed and, on failure, errorCode. Treat it as a UI signal only — the authoritative outcome is the webhook, which arrives even if the app is closed.

The SDK never sees points balances, card data, or your API key — only the pre-selection hints you pass and the embed token your backend mints.