Sambung dengan SAPRO
Beri pengguna sistem anda satu butang: pemilik syarikat log masuk ke SAPRO, pilih syarikat, tekan Benarkan, dan sistem anda boleh terus hantar jurnal dan baca laporan. Tiada token untuk disalin, tiada langkah manual.
Daftar aplikasi
Hubungi SAPRO dengan nama aplikasi, logo, alamat kembali (redirect URI) dan akses yang perlu. Anda terima client_id dan client_secret.
Tambah butang
Butang menghantar pemilik ke /oauth/authorize. SAPRO menghantar mereka kembali dengan kod, dan pelayan anda menukarnya kepada token.
Panggil API
Guna token sebagai Bearer pada https://sapro.my/api/v1. Token terikat kepada satu syarikat dan akses yang dibenarkan pemilik.
Aliran sambungan
OAuth 2.0 authorization code (RFC 6749), dengan PKCE S256 (RFC 7636) yang disyorkan. Semua panggilan token dari pelayan anda: client_secret tidak boleh berada dalam app telefon atau pelayar.
1. Hantar pemilik ke SAPRO
GET https://sapro.my/oauth/authorize
?response_type=code
&client_id=sapro_xxxxxxxxxxxxxxxxxxxxxxxx
&redirect_uri=https://sekolah.contoh.my/sapro/callback
&scope=accounts:read branches:read journals:write
&state=RAWAK_DISIMPAN_DALAM_SESI
&code_challenge=BASE64URL(SHA256(code_verifier))
&code_challenge_method=S256
redirect_urimesti sama tepat dengan yang didaftarkan. Untuk sistem yang memberi setiap pelanggan subdomain sendiri, SAPRO boleh mendaftarkanhttps://*.contoh.my/sapro/callback(satu subdomain sahaja).statewajib. Semak ia sama apabila pemilik kembali, untuk menghalang CSRF.scopedipisahkan dengan ruang; kalau tiada, semua akses yang didaftarkan untuk aplikasi anda diminta.- Tambah
&ui_locales=enuntuk skrin dalam Bahasa Inggeris.
2. Pemilik kembali ke sistem anda
https://sekolah.contoh.my/sapro/callback?code=KOD_SEKALI_GUNA&state=RAWAK_DISIMPAN_DALAM_SESI
https://sekolah.contoh.my/sapro/callback?error=access_denied&state=RAWAK_DISIMPAN_DALAM_SESI
Kod sah 10 minit dan hanya sekali. Ralat lain yang mungkin: invalid_scope, invalid_request, unsupported_response_type.
3. Tukar kod kepada token (dari pelayan)
curl -X POST https://sapro.my/oauth/token \
-u "CLIENT_ID:CLIENT_SECRET" \
-d grant_type=authorization_code \
-d code=KOD_SEKALI_GUNA \
-d redirect_uri=https://sekolah.contoh.my/sapro/callback \
-d code_verifier=CODE_VERIFIER_ASAL
{
"access_token": "12|q8mW...",
"token_type": "Bearer",
"scope": "accounts:read branches:read journals:write",
"company": { "id": 42, "name": "Splash Swim Academy Sdn Bhd" },
"api_base_url": "https://sapro.my/api/v1"
}
client_id dan client_secret juga boleh dihantar dalam badan borang. Simpan token secara encrypted. Ia tidak luput; ia berhenti apabila pemilik memutuskan sambungan (app SAPRO → Tetapan → Integrasi), apabila anda memulangkannya, atau apabila pemilik menyambung semula (token lama diganti).
4. Putuskan dari sistem anda
curl -X POST https://sapro.my/oauth/revoke -u "CLIENT_ID:CLIENT_SECRET" -d token=ACCESS_TOKEN
Skop (akses)
Pemilik melihat senarai ini dalam Bahasa Melayu sebelum menekan Benarkan. Minta yang perlu sahaja.
| Skop | Apa yang dibuka |
|---|---|
accounts:read | Lihat carta akaun |
branches:read | Lihat senarai cawangan |
journals:read | Lihat jurnal manual |
journals:write | Tambah, ubah dan padam jurnal manual |
reports:read | Lihat laporan kewangan (untung rugi, kunci kira-kira, lejar) |
login | Buka SAPRO dengan satu klik, untuk pengguna syarikat yang e-melnya sama |
Token juga sentiasa boleh memanggil GET /me (pengguna dan syarikat yang bersambung).
API
Asas: https://sapro.my/api/v1. Hantar Authorization: Bearer TOKEN dan Accept: application/json. Jumlah wang dalam ringgit (2 tempat perpuluhan), tarikh YYYY-MM-DD.
| Kaedah & laluan | Skop | Keterangan |
|---|---|---|
GET /me | semua | Pengguna, syarikat dan langganan. |
GET /accounts | accounts:read | Carta akaun (id, nama, kod, jenis). Petakan akaun anda kepada id di sini. |
GET /accounts/{id}, GET /accounts/types | accounts:read | Satu akaun; jenis akaun. |
GET /branches | branches:read | Cawangan syarikat. |
GET /journals | journals:read / write | Jurnal manual. Penapis: external_ref, from, to, search, per_page. |
GET /journals/{id} | journals:read / write | Satu jurnal dengan barisnya. |
POST /journals | journals:write | Jurnal baharu. Debit mesti sama dengan kredit. |
PUT /journals/{id} | journals:write | Ganti jurnal (badan yang sama seperti POST). |
DELETE /journals/{id} | journals:write | Padam jurnal. |
GET /reports/years | reports:read | Tahun kewangan yang ada. |
GET /reports/profit-loss | reports:read | Untung rugi. year, atau from + to. |
GET /reports/balance-sheet, /trial-balance | reports:read | Kunci kira-kira; imbangan duga. |
GET /reports/general-ledger | reports:read | Lejar satu akaun: account_id, dan tempoh. |
GET /reports/{laporan} | reports:read | Juga cash-flow, receivables-aging, sales-by-customer, sst-summary, day-book dan lain-lain. Tambah format=pdf atau csv. |
POST /integrations/login-links | login | Pautan sekali guna untuk buka SAPRO terus, sudah log masuk. email pengguna anda, path (pilihan). |
Hantar jurnal
POST /api/v1/journals
X-Branch-Id: 7 (pilihan: cawangan dari GET /branches)
{
"date": "2026-10-01",
"reference_number": "KELASPRO 2026-10-01",
"external_ref": "KP:s12:sales:2026-10-01:7",
"remark": "Yuran diinvois hari ini",
"lines": [
{ "account_id": 1201, "description": "Yuran diinvois", "debit": 1250.00 },
{ "account_id": 4101, "description": "Yuran kelas", "credit": 1250.00 }
]
}
Elak catatan berganda dengan external_ref (unik dalam syarikat, sehingga 120 aksara). Hantar semula rujukan yang sama menjawab 409 dengan jurnal sedia ada dalam data, jadi cuba semula selepas rangkaian terputus adalah selamat. Untuk membetulkan angka, cari dengan GET /journals?external_ref=... dan PUT.
Contoh (PHP, Laravel)
$sapro = Http::withToken($token)->acceptJson()->baseUrl('https://sapro.my/api/v1');
$accounts = $sapro->get('accounts')->json('data');
$response = $sapro->post('journals', $journal);
if ($response->status() === 409) {
$existing = $response->json('data'); // sudah dihantar sebelum ini
}
Buka SAPRO dengan satu klik
Dengan skop login, sistem anda boleh meletakkan butang "Buka SAPRO" yang membawa pengguna terus masuk, tanpa kata laluan. Pelayan anda meminta pautan untuk e-mel pengguna yang menekan butang; SAPRO hanya memberi pautan jika e-mel itu milik pengguna syarikat yang bersambung (pemilik atau ahli pasukan). Pautan berfungsi sekali, dalam 2 minit. Buka pautan itu terus (redirect); jangan simpan.
POST /api/v1/integrations/login-links
{ "email": "pengguna@sekolah.my", "path": "/reports/profit-loss" }
201 { "data": { "url": "https://sapro.my/app/#/sso?code=...", "expires_at": "..." } }
404 { "code": "no_user" } e-mel itu bukan pengguna syarikat ini
403 { "code": "integration_scope" } sambungan tanpa skop login: minta pemilik sambung semula
path ialah halaman dalam app SAPRO, contohnya /reports/profit-loss, /reports/sst-summary atau /journals/{id} (jurnal yang anda hantar).
Ralat
| Status | Maksud | Apa perlu dibuat |
|---|---|---|
401 | Token tidak sah: pemilik memutuskan sambungan, atau token telah diganti. | Tunjuk "Sambung dengan SAPRO" semula. |
402 subscription_required | Langganan SAPRO syarikat telah tamat. Bacaan masih berfungsi. | Beritahu pengguna; cuba semula kemudian. |
403 integration_scope | Pemilik tidak memberi akses ini. | Minta skop yang betul semasa menyambung. |
403 integration_app_stopped | Aplikasi anda dihentikan oleh SAPRO. | Hubungi SAPRO. |
409 duplicate | external_ref sudah digunakan. | Guna jurnal dalam data. |
422 | Data tidak sah; butiran dalam errors. | Betulkan dan hantar semula. |
429 | Terlalu banyak panggilan (120 seminit bagi setiap sambungan). | Tunggu dan cuba semula. |
Mesej ralat dalam message mengikut bahasa Accept-Language (ms atau en).