SDK React Native
Lihat di GitHubPaket @tracive/sdk-react-native, versi 0.1.0, mode OAuth saja di Tahap 1, untuk React Native 0.76 ke atas dan Expo. TypeScript murni tanpa kode native sama sekali, jadi tidak ada autolinking dan tidak terikat pada arsitektur lama atau baru.
Pasang
Paket pendampingnya bukan opsional. Sumber acak kriptografis dipakai oleh setiap pengecekan, bukan hanya PKCE; tanpanya pengecekan gagal dengan csprng_unavailable, dan SDK sengaja tidak mundur ke Math.random(). SQLite dan secure store dipakai untuk cache persisten dan penyimpanan token.
npm install @tracive/sdk-react-native
npx expo install expo-crypto expo-sqlite expo-secure-storeMemasang paketnya saja belum cukup. Di Expo, berikan expoRandom sebagai random ke cache, client, dan TraciveOAuth, seperti contoh di bawah: tidak ada paket Expo yang mengisi globalThis.crypto hanya dengan diimpor. Di React Native bare, impor react-native-get-random-values di baris paling atas file entry Anda.
Setup aplikasi host
Semua yang harus dideklarasikan aplikasi host menyebut domain Anda, yang tidak bisa diketahui library. Dengan Expo prebuild, config plugin menuliskan semuanya:
{
"expo": {
"plugins": [
["@tracive/sdk-react-native/app.plugin.js", { "redirectUri": "https://your-app.example/oauth/callback" }]
]
}
}Tanpa prebuild, tambahkan sendiri: blok <queries> untuk skema tracive, tracivesandbox, dan https di manifest Android, intent-filter App Link di activity ber-singleTask, serta LSApplicationQueriesSchemes dan Associated Domains di iOS. Lalu periksa hasilnya:
npx tracive-doctor --redirect-uri https://your-app.example/oauth/callbacktracive-doctor membaca manifest, Info.plist, entitlements, dan app.json, mencetak apa yang kurang beserta blok untuk ditempel, dan keluar dengan kode 1 kalau ada yang fatal. Pasang di CI.
- · Deklarasi yang hilang tidak menimbulkan crash, log, atau error: setiap pengguna yang sudah memasang Tracive diam-diam dikirim ke halaman install. Itu kesalahan integrasi yang paling mungkin terjadi, dan yang paling mahal.
- ·
assetlinks.jsondanapple-app-site-associationdi domain Anda tetap harus Anda sajikan sendiri; tidak ada plugin yang bisa menuliskannya.
exp:// yang berubah mengikuti alamat dev server, dan pemeriksaan aplikasi terpasang di iOS selalu menjawab tidak. Pakai development build. Query, model, dan cache tetap jalan di Expo Go untuk prototipe.Membuat client
Satu instance per aplikasi. Token store dan penyimpan otorisasi yang tertunda bawaannya ada di memori, jadi berikan versi secure store: tanpanya grant hilang setiap aplikasi dimulai ulang, dan otorisasi yang terputus karena Android mematikan proses tidak bisa diselesaikan.
import { TraciveClient, TraciveEnvironment, TraciveMode, TraciveOAuth, TraciveScopes } from '@tracive/sdk-react-native';
import {
expoRandom,
expoSecurePendingAuthorizationStore,
expoSecureTokenStore,
openExpoCheckCache,
} from '@tracive/sdk-react-native/expo'; // bare: '@tracive/sdk-react-native/bare'
const config = {
clientId: 'trcvc_…', // dari portal
redirectUri: 'https://app-anda.example/oauth/callback', // sama persis dengan yang terdaftar
partnerUserId: customerId, // id Anda sendiri, buram dan stabil
scopes: [TraciveScopes.scammerCheck, TraciveScopes.profileRead],
};
const environment = TraciveEnvironment.sandbox; // production adalah bawaannya
export const client = new TraciveClient({
mode: TraciveMode.oauth(config),
environment,
cache: await openExpoCheckCache({ random: expoRandom }),
random: expoRandom, // Expo saja; bare memakai polyfill
oauth: new TraciveOAuth({
mode: config,
environment, // tidak diwarisi dari client
tokenStore: expoSecureTokenStore(),
pendingStore: expoSecurePendingAuthorizationStore(),
random: expoRandom,
}),
});TraciveOAuth yang Anda buat sendiri tidak mewarisi environment dari client. Berikan ke keduanya: client di sandbox dengan TraciveOAuth tanpa environment tetap mengotorisasi ke production.client_secret juga tidak boleh ikut di aplikasi mobile; itulah gunanya tipe client CONFIDENTIAL.Otorisasi
Consent terjadi di aplikasi Tracive, dan perjalanan antar-aplikasi itu bisa lebih panjang dari umur proses Anda. Karena itu hasilnya tidak dikembalikan authorize(), tetapi tiba di onAuthResult. startLinkListener() aman menerima setiap link: yang bukan redirect Anda diabaikan tanpa menyentuh otorisasi yang sedang berjalan.
// Sekali, saat aplikasi mulai: menampung link hangat dan link cold start.
const stop = client.startLinkListener();
client.onAuthResult((result) => {
switch (result.type) {
case 'authorized': onConnected(result.grantedScopes); break;
case 'appNotInstalled': showInstallHint(); break; // halaman install sudah dibuka
case 'denied': respectThat(); break;
case 'codeReceived': sendToOwnBackend(result.code, result.codeVerifier, result.redirectUri); break;
case 'failed': report(result.error); break;
}
});
// Menyelesaikan null saat halaman consent terbuka. Hasilnya tiba di listener di atas.
await client.authorize();Mengecek
TraciveQuery.phone hanya menerima E.164; normalizePhoneNumber menambahkan kode negaranya. Builder dengan lebih dari satu field menerima objek, supaya dua string tidak tertukar diam-diam: bankAccount({ countryAlpha3, bankCode, account }), crypto({ chain, address }), username({ platform, handle }). Tersedia juga email dan iban. Untuk menyebut bank dengan kode nasional atau BIC, ganti bankCode dengan bankShortCode atau bic di objek yang sama. Kode yang tidak ada di daftar kode bank ditolak di perangkat dengan unknown_bank_code, jadi tidak pernah ditagih.
import { isTraciveQueryError, normalizePhoneNumber, TraciveQuery } from '@tracive/sdk-react-native';
let query;
try {
query = TraciveQuery.phone(normalizePhoneNumber('IDN', input)); // "0857-1860-9079" → +6285718609079
} catch (error) {
if (isTraciveQueryError(error)) return showInputError(error.reason); // tidak ada yang terkirim, tidak ditagih
throw error;
}
const result = await client.checkScammer(query);
render(result.riskTier, result.reportTotal, result.disclaimer); // disclaimer wajib tampilInput yang mustahil cocok ditolak sebelum ada yang terkirim, karena miss tetap ditagih. Aturannya ada di grammar input.
Cache
Tanpa argumen cache, SDK menyimpan di memori, dan setiap aplikasi dimulai ulang hasil yang sudah dibayar ikut hilang. Verdict HIGH di-cache 30 hari, jadi itu 30 hari tagihan berulang untuk identifier yang sama. openExpoCheckCache() dan openBareCheckCache() tidak pernah melempar error; kalau gagal dibuka, cache-nya selalu miss dan errornya ada di cache.lastStoreError.
Cache yang sama juga mencatat hasil yang ditunjukkan pada satu transaksi, supaya layar detail transaksi tetap bisa menampilkannya kemudian tanpa pengecekan kedua. Setiap catatan adalah snapshot dengan retensi 90 hari. Jangan kirim hasil ini ke server Anda: ketentuan partner melarangnya.
// Setelah transfer tercipta dan punya nomor referensi.
await client.cache.writeTransactionCheck(result, transfer.reference);
// Di detail transaksi, kapan pun kemudian. undefined = tidak ada pengecekan; jangan tampilkan apa-apa.
const shown = await client.cache.readTransactionCheck(transfer.reference);
if (shown !== undefined) renderSecurityCheckCard(shown);Error & biaya
error.kind menyebut kasusnya. Token kedaluwarsa tidak perlu Anda tangani: SDK me-refresh lalu mengulang sekali.
try {
await client.checkScammer(query, { idempotencyKey: attemptId }); // satu kunci per pengecekan
} catch (error) {
if (!isTraciveError(error)) throw error;
switch (error.kind) {
case 'insufficientCredits': promptTopUp(); break; // 402: jangan cek credits() dulu
case 'grantRevoked':
case 'invalidToken': reauthorize(); break; // grant hilang atau belum ada
case 'rateLimited': backOff(error.retryAfterSeconds); break;
default: report(error);
}
}- · Setiap request yang sampai ke server ditagih, termasuk miss.
- · Tanpa
idempotencyKey, SDK tidak mengulang 5xx, 429, atau timeout: ia tidak bisa tahu apakah percobaan pertama sudah ditagih. - · Jangan panggil
credits()sebelum setiap pengecekan. Tangani 402-nya.
Keluar
await client.signOut(); // revoke best-effort, lalu token dan cache dihapus apa pun hasilnya