SDK Flutter

Lihat di GitHub

Paket tracive_sdk, versi 0.1.0, mode OAuth saja di Tahap 1, untuk Android dan iOS. Permukaannya sengaja sepadan dengan SDK Kotlin, Swift, dan React Native, sehingga integrasi lintas platform membaca sama.

Pasang

Belum terbit di pub.dev. Baris di bawah adalah yang berlaku begitu rilis 0.1.0 terbit.
dependencies:
  tracive_sdk: ^0.1.0   # Dart ^3.5.0, Flutter >=3.24.0

Setup Android & iOS

Paket ini Dart murni, bukan plugin, jadi ia tidak bisa menyisipkan apa pun ke manifest atau Info.plist Anda. Tanpa deklarasi di bawah, SDK tidak bisa melihat apakah aplikasi Tracive terpasang, dan setiap pengguna dikirim ke halaman install tanpa ada error apa pun.

<!-- AndroidManifest.xml, anak langsung dari <manifest> -->
<queries>
  <intent>   <!-- aplikasi Tracive production -->
    <action android:name="android.intent.action.VIEW" />
    <data android:scheme="tracive" />
  </intent>
  <intent>   <!-- build sandbox -->
    <action android:name="android.intent.action.VIEW" />
    <data android:scheme="tracivesandbox" />
  </intent>
  <intent>   <!-- Custom Tab dan halaman install -->
    <action android:name="android.intent.action.VIEW" />
    <data android:scheme="https" />
  </intent>
</queries>

<!-- Di dalam <activity> Anda, yang ber-launchMode="singleTask" -->
<intent-filter android:autoVerify="true">
  <action android:name="android.intent.action.VIEW" />
  <category android:name="android.intent.category.DEFAULT" />
  <category android:name="android.intent.category.BROWSABLE" />
  <data android:scheme="https" android:host="app-anda.example" android:pathPrefix="/oauth/callback" />
</intent-filter>
  • · Daftarkan tracivesandbox juga. Pemeriksaan kehadiran aplikasi mengikuti environment, jadi yang terlupa hanya merusak sandbox, dan itu yang paling sulit ketahuan.
  • · Tidak perlu entri <package>. Pemeriksaannya memakai skema, dan satu nama package tidak bisa mengikuti environment.
  • · Redirect Anda sebaiknya App Link dan Universal Link di domain milik Anda, lengkap dengan assetlinks.json dan apple-app-site-association. Skema kustom bisa diklaim aplikasi lain di perangkat yang sama.
Jangan membuka URL authorize di WebView dalam aplikasi. App Link tidak menyala dari WebView, jadi redirect-nya tidak pernah kembali dan pengguna terdampar di halaman fallback meski aplikasi Tracive terpasang.

Membuat client

Satu instance per aplikasi. Pakai konstanta TraciveScopes, bukan string: server menolak seluruh permintaan consent kalau satu nama scope tidak dikenalnya.

final client = TraciveClient(
  mode: TraciveMode.oauth(
    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: const [TraciveScopes.scammerCheck, TraciveScopes.profileRead],
  ),
  environment: TraciveEnvironment.sandbox,                    // production adalah bawaannya
  cache: await openTraciveCache(),                            // SQLCipher, kunci di Keystore/Keychain
);
Tidak ada parameter API key di mana pun, di mode mana pun. Sama seperti SDK lainnya: kunci yang dikirim bersama aplikasi adalah kunci yang sudah dipublikasikan.

Otorisasi

Dua panggilan. authorize() membuka layar consent lalu selesai; handleCallback() yang menyelesaikan otorisasinya saat redirect tiba. Aman mengalirkan setiap deep link ke sana: null berarti “bukan milik saya”, dan tidak ada yang disentuh.

final start = await client.authorize();
switch (start) {
  case null:                       break;  // halaman terbuka, tunggu redirect
  case AuthAppNotInstalled():      showInstallHint();
  case AuthFailed(:final error):   report(error);
  default:                         break;
}

Mengecek

TraciveQuery.phone hanya menerima E.164; normalizePhoneNumber menambahkan kode negaranya, dari kode alpha-3 (IDN) atau kode telepon (+62). Tersedia juga .email, .bankAccount, .iban, .crypto, dan .username. Untuk menyebut bank dengan kode nasional atau BIC, pakai .bankAccountByShortCode atau .bankAccountByBic. Kode yang tidak ada di daftar kode bank ditolak di perangkat dengan unknownBankCode, jadi tidak pernah ditagih.

final TraciveQuery query;
try {
  query = TraciveQuery.phone(normalizePhoneNumber('IDN', input));  // "0857-1860-9079" → +6285718609079
} on TraciveQueryException catch (e) {
  return showInputError(e.reason);                                 // tidak ada yang terkirim, tidak ditagih
}

final result = await client.checkScammer(query);
render(result.riskTier, result.reportTotal, result.disclaimer);    // disclaimer wajib tampil

Semuanya melipat identifier di klien dengan aturan yang sama dengan server, dan menolak input yang mustahil cocok sebelum ada yang terkirim, karena miss tetap ditagih. Rinciannya di grammar input.

Cache

Tanpa argumen cache, client memakai cache di memori yang hilang setiap aplikasi dimulai ulang, dan setiap pengecekan ulang setelahnya ditagih lagi. openTraciveCache() di contoh di atas membuka cache persisten, dan tidak pernah melempar error: kalau gagal dibuka, cache-nya selalu miss dan errornya ada di cache.lastStoreError. Pantau itu, karena cache yang mati berarti setiap pengecekan memakan kredit.

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(transfer.reference, result);

// Di detail transaksi, kapan pun kemudian. null = tidak ada pengecekan; jangan tampilkan apa-apa.
final shown = await client.cache.readTransactionCheck(transfer.reference);
if (shown != null) renderSecurityCheckCard(shown);

Error & biaya

Setiap error.code dari server menjadi subclass TraciveException. Token kedaluwarsa tidak perlu Anda tangani: SDK me-refresh lalu mengulang sekali.

try {
  await client.checkScammer(query, idempotencyKey: attemptId);   // satu kunci per pengecekan
} on TraciveInsufficientCreditsException {
  promptTopUp();                  // 402: jangan cek credits() sebelum setiap pengecekan
} on TraciveGrantRevokedException {
  reauthorize();                  // token sudah dihapus; jangan diulang
} on TraciveInvalidTokenException {
  reauthorize();                  // belum ada grant yang bisa dipakai; tidak ada yang terkirim
} on TraciveRateLimitedException catch (e) {
  backOff(e.retryAfter);          // tidak pernah diulang otomatis
}
  • · Setiap request yang sampai ke server ditagih, termasuk miss.
  • · SDK hanya mengulang pengecekan yang gagal kalau Anda memberi idempotencyKey: 5xx atau timeout tidak memberi tahu apakah pengecekan 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

Kenapa mode-nya bukan const

TraciveOAuthMode sengaja bukan konstruktor const, dan alasannya layak diketahui karena ia menjelaskan satu kelas bug yang sulit dilacak.

Pemeriksaan partnerUserId yang kosong dulu berupa assert, dan compiler AOT membuang assert. Akibatnya build release mengirim partner_user_id= kosong ke jaringan sementara build debug menolaknya, dan selisih itu baru muncul di production. Konstruktor const juga tidak bisa memanggil trim(), jadi ia tidak akan pernah menangkap id berisi spasi.

Kehilangan const memakan satu kata kunci; mempertahankannya memakan pemeriksaannya.