mdestafadilah / satusehat-sdk
Unofficial API wrapper package to integrate with SATUSEHAT platform (fork of agumil/satusehat-sdk with PR #13 updates).
Package info
github.com/mdestafadilah/satusehat-sdk-forking
pkg:composer/mdestafadilah/satusehat-sdk
Requires
- php: >=8.0
- guzzlehttp/guzzle: ^7.16
- guzzlehttp/promises: ^2.6
- guzzlehttp/psr7: ^2.13
- psr/log: ^1.1 || ^2.0 || ^3.0
- vlucas/phpdotenv: ^5.6
Requires (Dev)
- phpunit/phpunit: ^9.6 || ^10.5 || ^11.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
API wrapper tidak resmi untuk integrasi dengan platform SATUSEHAT (Kementerian Kesehatan RI).
FHIR R4 · Consent · KFA · KYC · OAuth2
composer require mdestafadilah/satusehat-sdk
Tentang Proyek • Sumber Kode • PR #13 • Catatan • Fitur • Instalasi • Mulai Cepat • Konfigurasi • Partial Update • Paging • KYC • Logging • Token Caching • Pengujian • Lisensi
Tentang Proyek
mdestafadilah/satusehat-sdk adalah pustaka PHP (tanpa framework) untuk berbicara dengan platform SATUSEHAT milik Kementerian Kesehatan RI. Paket ini adalah fork dari agumil/satusehat-sdk. Pustaka ini membungkus lima kelompok API yang dipakai faskes sehari-hari:
| Modul | Client | Base path | Kegunaan |
|---|---|---|---|
| FHIR R4 | SSClient |
/fhir-r4/v1 |
Resource klinis: Patient, Encounter, Observation, Condition, Medication, dan 24 resource lain (29 kelas resource di src/FHIR). |
| Consent | SSClient |
/consent/v1 |
Persetujuan pasien atas pertukaran data. |
| KFA | SSClientKFA |
/kfa, /kfa-v2, /kfa-v3 |
Katalog produk & alkes, harga JKN, dan template. |
| KYC | SSClientKYC |
/kyc/v1 |
Verifikasi NIK untuk memperoleh IHS Number. |
| OAuth2 | Oauth2 |
/oauth2/v1 |
Pengambilan dan penyegaran access token. |
Semua modul berbagi satu token dan satu mekanisme transport, sehingga retry, timeout, logging, dan penyamaran data pasien berlaku seragam di seluruh permukaan API.
Repo ini adalah fork dari proyek asal. Isinya bukan sekadar salinan: seluruh perubahan pada PR #13 sudah masuk, ditambah perbaikan dokumentasi ini. Rincian asal-usul kode ada di Sumber Kode.
Sumber Kode
| Peran | Lokasi |
|---|---|
| Proyek asal (upstream) | agumil/satusehat-sdk |
| Pull request yang menjadi dasar fork ini | agumil/satusehat-sdk#13 — "Bunch of Update" |
| Branch asal PR | hallobayi/satusehat-sdk@main |
| Repo fork ini | mdestafadilah/satusehat-sdk-forking |
| Analisis keamanan kode ini | ANALISIS-KEAMANAN.md |
PR #13 berstatus terbuka (belum di-merge) saat README ini ditulis: 89 berkas berubah, +5.551 / −281 baris, dikerjakan oleh mdestafadilah. Versi yang berisi seluruh perbaikan di bawah dirilis di Packagist dengan nama mdestafadilah/satusehat-sdk — lihat Instalasi.
Apa yang Dibawa PR #13
PR ini mengubah SDK dari "pembungkus HTTP" menjadi pustaka yang layak dipakai di fasilitas kesehatan. Ringkasannya:
| Area | Perubahan | Berkas kunci |
|---|---|---|
| Ketahanan transport | Retry otomatis untuk 429/5xx dengan dukungan Retry-After dan exponential backoff; hanya method idempoten yang diulang sehingga POST tidak pernah terkirim dua kali. Semua kegagalan transport dikembalikan sebagai Response, tidak bocor sebagai exception Guzzle. |
src/Request/HttpRequest.php |
| OAuth2 | Endpoint token punya timeout/connect_timeout sendiri (15s/5s); kegagalan jaringan menjadi SSOauth2Exception; client_secret disaring dari pesan error; refresh otomatis sekali saat menerima 401. |
src/Auth/Oauth2.php |
| Token store | TokenStoreInterface dengan dua implementasi: InMemoryTokenStore (default) dan FileTokenStore (berbagi token antar worker PHP-FPM). File ditulis 0600 secara atomik, diverifikasi fileperms(), disegel HMAC memakai client_secret, dan refresh-nya dikunci flock. |
src/Auth/FileTokenStore.php, src/Auth/InMemoryTokenStore.php |
| Privasi (PHI) | PhiRedactor bekerja default-deny: seluruh nilai diganti [REDACTED], hanya nama kunci, resourceType, status, dan parameter paging yang bertahan. ID resource di path URL juga disamarkan. |
src/Helper/PhiRedactor.php |
| Modul KYC | Modul baru: generateUrl(), generateChallengeCode(), requestPerNik(). X-Debug-Mode ditolak di production (SSEnvException dilempar sebelum request dibuat); NIK divalidasi 16 digit sebelum dikirim. |
src/KYC/, src/SSClientKYC.php |
| Paging | BundlePager menelusuri link.next/_page_token tanpa perlu menyusun URL sendiri, dengan pengaman halaman berulang dan batas jumlah halaman. BundleHelper untuk membaca Bundle yang sudah ter-decode. |
src/Helper/BundlePager.php, src/Helper/BundleHelper.php |
| FHIR & KFA | Resource baru (ImagingStudy, Medication, Slot, Patient::patch), validasi ID sebelum interpolasi URL, serta perapian base KFA dan endpoint Price. |
src/FHIR/, src/KFA/ |
| Pengujian | 43 test / 78 assertion, tanpa akses jaringan (Guzzle di-mock, transport KYC/FHIR diganti recorder). | tests/ |
| CI | GitHub Actions: matriks PHP 8.0–8.4, composer update --prefer-lowest, dan composer audit. |
.github/workflows/ci.yml |
| Dependensi | Guzzle 7.16 + psr7 2.13 + promises 2.6 (Guzzle 6 yang sudah EOL ditinggalkan), vlucas/phpdotenv 5.6, psr/log 1–3. |
composer.json |
| Dokumentasi | README ini dan ANALISIS-KEAMANAN.md (audit keamanan dengan bukti eksekusi). |
README.md, ANALISIS-KEAMANAN.md |
Catatan
Paket ini masih dalam pengembangan. Dokumentasi terbatas dan beberapa komponen belum lengkap. Uji dulu di environment development sebelum menyentuh produksi.
Fitur Utama
- Payload Builder — susun payload senyata mungkin dengan builder; tidak suka? Boleh diabaikan dan kirim array biasa.
- Sederhana — dependensi seminimal mungkin; kelas resource mengikuti standar HL7 FHIR.
- Fleksibel — tidak terikat framework tertentu; payload bisa dikirim dengan gaya apa pun sesuai proyek Anda.
- Tangguh secara default — retry otomatis untuk
429/5xxdenganRetry-Afterdan exponential backoff; hanya method idempoten yang diulang. - Aman secara default — ID resource divalidasi sebelum masuk URL, dan PHI disamarkan sebelum menyentuh log.
- Helper praktis —
BundlePageruntuk menelusuri halaman FHIR,FileTokenStoreagar worker PHP-FPM berbagi satu access token.
Kebutuhan Sistem
| Komponen | Versi |
|---|---|
| PHP | >= 8.0 (diuji 8.0 – 8.4) |
guzzlehttp/guzzle |
^7.16 |
guzzlehttp/psr7 |
^2.13 |
guzzlehttp/promises |
^2.6 |
vlucas/phpdotenv |
^5.6 (wajib, dipakai EnvHelper) |
psr/log |
^1.1 || ^2.0 || ^3.0 (opsional saat dipakai) |
Guzzle 6 tidak lagi didukung: sudah EOL dan punya advisory tanpa perbaikan. Guzzle 8 juga belum didukung karena memerlukan psr7 3 dan promises 3 — migrasi tersendiri.
Instalasi
composer require mdestafadilah/satusehat-sdk
Bila hanya butuh versi upstream apa adanya:
composer require agumil/satusehat-sdk
Untuk mengikuti branch main tanpa menunggu rilis (mis. ingin ikut perubahan yang belum di-tag):
composer config repositories.satusehat-forking vcs https://github.com/mdestafadilah/satusehat-sdk-forking composer require mdestafadilah/satusehat-sdk:dev-main
Atau langsung di composer.json:
{
"repositories": [
{ "type": "vcs", "url": "https://github.com/mdestafadilah/satusehat-sdk-forking" }
],
"require": {
"mdestafadilah/satusehat-sdk": "dev-main"
}
}
Simpan kredensial di .env:
SATUSEHAT_ENV=development|staging|production SATUSEHAT_ORGANIZATION_ID=organization_id SATUSEHAT_CLIENT_ID=client_id SATUSEHAT_CLIENT_SECRET=client_secret
SATUSEHAT_ENV menentukan host untuk seluruh modul (FHIR, Consent, KFA, KYC, dan OAuth2): development → api-satusehat-dev.dto.kemkes.go.id, staging → api-satusehat-stg.dto.kemkes.go.id, production → api-satusehat.kemkes.go.id. Nilai .env didahulukan; parameter 'environment' pada config hanya dipakai bila .env tidak diisi.
Catatan Logging bersifat opsional. SDK hanya bergantung pada
psr/log(antarmuka saja) dan tidak menulis apa pun kecuali Anda memberikan instance logger. Logger PSR-3 apa pun bisa dipakai —composer require monolog/monologbila belum ada.
Mulai Cepat
Dengan PHPDOTENV:
use agumil\SatuSehatSDK\Auth\Oauth2; use agumil\SatuSehatSDK\SSClient; $ssclient = new SSClient(new Oauth2()); $response = $ssclient->getPractitioner([ 'identifier' => 'https://fhir.kemkes.go.id/id/nakes-his-number|10009880728', ]);
Tanpa PHPDOTENV:
use agumil\SatuSehatSDK\Auth\Oauth2; use agumil\SatuSehatSDK\SSClient; $config1['environment'] = 'development'; $config1['client_id'] = 'your_client_id'; $config1['client_secret'] = 'your_client_secret'; $config2['environment'] = 'development'; $ssclient = new SSClient(new Oauth2($config1), $config2); $response = $ssclient->getPractitioner([ 'identifier' => 'https://fhir.kemkes.go.id/id/nakes-his-number|10009880728', ]);
Host ditentukan oleh environment, bukan oleh URL yang Anda masukkan. Tidak ada kunci base_url: bila URL bisa diatur pemanggil, satu payload yang salah dapat mengarahkan Bearer token ke host lain (SSRF).
Contoh lain yang siap dijalankan ada di direktori examples/ (bootstrap.php memuat autoloader dan .env).
Konfigurasi
Argumen kedua SSClient diteruskan ke setiap request. Semua kunci opsional dan nilai default-nya aman dipakai apa adanya.
use agumil\SatuSehatSDK\Auth\Oauth2; use agumil\SatuSehatSDK\SSClient; $ssclient = new SSClient(new Oauth2(), [ 'timeout' => 30, 'logger' => $logger, 'retry_attempts' => 3, 'retry_base_delay_ms' => 200, 'retry_max_delay_ms' => 5000, 'retry_methods' => ['GET', 'PUT', 'DELETE'], ]);
| Kunci | Tipe | Default | Keterangan |
|---|---|---|---|
timeout |
int |
60 |
Timeout request dalam detik. |
logger |
Psr\Log\LoggerInterface |
null |
Logger PSR-3 opsional. Tanpa ini, tidak ada yang dicatat. |
retry_attempts |
int |
3 |
Total percobaan termasuk percobaan pertama. Isi 1 untuk mematikan retry. |
retry_base_delay_ms |
int |
200 |
Jeda dasar untuk exponential backoff. |
retry_max_delay_ms |
int |
5000 |
Batas atas jeda hasil perhitungan backoff. |
retry_methods |
string[] |
['GET', 'PUT', 'DELETE'] |
Method HTTP yang boleh diulang. |
Perilaku Retry
Request hanya diulang bila dua syarat terpenuhi:
- Status respons
429,500,502,503, atau504. - Method HTTP terdaftar di
retry_methods.
4xx lain (400, 401, 403, 404) tidak pernah diulang karena pengulangannya menghasilkan jawaban yang sama. POST dikecualikan secara default: bila request pertama sebenarnya berhasil tetapi responsnya hilang, mengulang akan membuat resource tercatat dua kali. Tambahkan 'POST' ke retry_methods hanya bila endpoint Anda memang aman diduplikasi.
Bila server mengirim header Retry-After, nilainya dihormati (dibatasi 60 detik). Jika tidak, SDK menunggu retry_base_delay_ms * 2^(attempt - 1) dengan jitter, tidak melebihi retry_max_delay_ms. Setelah retry_attempts habis, respons terakhir dikembalikan apa adanya — tidak diubah menjadi exception, sehingga $response->getHttpCode() dan $response->getErrorMessages() tetap bisa dipakai.
Partial Update
createPatient() selalu mengirim seluruh resource. Untuk mengubah satu field tanpa mengirim ulang semuanya, gunakan updatePatient() yang mengirim dokumen JSON Patch (RFC 6902):
$response = $ssclient->updatePatient('P02478375538', [ [ 'op' => 'replace', 'path' => '/language', 'value' => 'id', ], ]); var_dump($response->getContentAsObject());
SATUSEHAT saat ini hanya menerima operasi replace. Beberapa operasi boleh dikirim dalam satu array, dan setiap path relatif terhadap akar resource (misalnya /name/0/text). Contoh lengkap: examples/FHIR/Patient/patch.php.
Paging
Pencarian FHIR mengembalikan Bundle yang link.next-nya berisi _page_token buram. Anda tidak bisa menghitung halaman berikutnya sendiri — parameternya harus dikirim ulang dari link tersebut. BundlePager mengerjakannya untuk Anda:
use agumil\SatuSehatSDK\Helper\BundleHelper; use agumil\SatuSehatSDK\Helper\BundlePager; $pager = new BundlePager( fn (array $params) => $ssclient->getPatient($params), ['identifier' => 'https://fhir.kemkes.go.id/id/nik|9271060312000001', '_count' => 100] ); while (($response = $pager->next()) !== null) { foreach (BundleHelper::entries($response->getContentAsArray()) as $entry) { echo $entry['resource']['id'] . PHP_EOL; } } echo 'Halaman terambil: ' . $pager->page() . PHP_EOL;
next() mengembalikan null saat tidak ada halaman berikutnya. Ia juga berhenti ketika request gagal, atau ketika server mengembalikan link yang sama dua kali (pengaman terhadap loop tanpa akhir). Argumen ketiga yang opsional membatasi jumlah halaman, default 1000.
BundleHelper berguna sendiri saat Anda sudah punya Bundle ter-decode:
BundleHelper::isBundle($content); // bool BundleHelper::entries($bundle); // list entri, atau [] BundleHelper::total($bundle); // int|null BundleHelper::nextUrl($bundle); // string|null BundleHelper::nextParams($bundle); // array|null berisi query param
KYC
KYC (verifikasi NIK) punya base URL sendiri (/kyc/v1) dengan Bearer token yang sama, jadi ia dipakai lewat client terpisah: SSClientKYC.
Alurnya tiga langkah, dan SDK hanya mengerjakan dua di antaranya — enkripsi dan dekripsi tetap di sisi aplikasi karena kuncinya dipegang faskes:
- Enkripsi pesan permintaan memakai kunci publik dari Kemkes (di luar SDK).
generateUrl()menukar pesan terenkripsi itu dengan sesi KYC. Responsnya masih ciphertext; dekripsikan untuk mengambilurl_token.generateChallengeCode()ataurequestPerNik()meminta challenge code memakaiurl_tokentersebut.
use agumil\SatuSehatSDK\Auth\Oauth2; use agumil\SatuSehatSDK\SSClientKYC; $kyc = new SSClientKYC(new Oauth2(), ['environment' => 'development']); $url_response = $kyc->generateUrl($encrypted_message); // text/plain, masih ciphertext $url_token = /* hasil dekripsi di sisi aplikasi */; $response = $kyc->requestPerNik('3171022809990001', 'Budi Santoso', $url_token); if ($response->getHttpCode() === 200) { $data = $response->getContentAsObject()->data; echo $data->ihs_number; // IHS Number pasien pemilik NIK echo $data->challenge_code; // 6 digit echo $data->expired_timestamp; // batas waktu challenge code }
| Method | Endpoint | Keterangan |
|---|---|---|
generateUrl(string $encryptedMessage) |
POST /kyc/v1/generate-url |
Body dikirim apa adanya sebagai text/plain. |
generateChallengeCode(array $params, string $urlToken, bool $debugMode = false) |
POST /kyc/v1/challenge-code |
$params berisi ['metadata' => [...], 'data' => [...]]. |
requestPerNik(string $nik, string $name, string $urlToken, bool $debugMode = false) |
POST /kyc/v1/challenge-code |
Pintasan dengan metadata.method = request_per_nik. |
Beberapa hal yang perlu diketahui:
$urlTokendikirim sebagai headerX-Frame-Token. SDK tidak menyimpannya, jadi ambil dari responsgenerate-url()lalu teruskan sendiri.$debugMode = truemenambahkanX-Debug-Mode: 1supaya challenge code dikembalikan langsung tanpa widget KYC. Pakai hanya di development/staging: mode ini melewati verifikasi yang seharusnya dilakukan pasien, jadi SDK menolaknya sendiri (SSEnvException) bila environment-nyaproduction— request tidak dikirim sama sekali.- NIK diperiksa 16 digit sebelum dikirim, karena respons memuat
ihs_numbermilik NIK tersebut — salah ketik berarti challenge code untuk orang lain. - Lingkungan mengikuti
SATUSEHAT_ENV(.envdidahulukan) atau'environment'pada config. Nilai yang sah:development,staging,production— sama seperti modul lain.
Contoh: examples/KYC/generate_url.php dan examples/KYC/request_per_nik.php.
Logging
Berikan logger PSR-3 apa pun dan setiap pertukaran HTTP akan dicatat. Data pasien disamarkan lebih dulu, sehingga identifier, nama, tanggal lahir, alamat, dan kontak tidak pernah sampai ke berkas log Anda.
use Monolog\Handler\StreamHandler; use Monolog\Logger; $logger = new Logger('satusehat'); $logger->pushHandler(new StreamHandler(__DIR__ . '/satusehat.log', Logger::DEBUG)); $ssclient = new SSClient(new Oauth2(), ['logger' => $logger]);
Yang dicatat:
| Level | Kapan |
|---|---|
debug |
Setiap pertukaran selesai: method, URL tersamarkan, status, durasi. |
warning |
Kegagalan yang akan diulang, termasuk jeda yang dipakai. |
error |
Request gagal permanen, atau seluruh retry sudah habis. |
Query string dan body JSON dilewatkan ke PhiRedactor, yang bekerja secara default-deny: setiap nilai diganti [REDACTED] dan hanya nama kunci, sejumlah kecil field struktural (resourceType, status), serta parameter paging (_count, _offset, ...) yang bertahan. Menyamarkan daftar kunci "sensitif" tidak cukup untuk FHIR, karena identitas juga berada di tempat yang nama kuncinya tampak lugu — subject.display, text.div, dan note.text semuanya pernah membawa nama dan NIK pasien pada versi sebelumnya. ID resource di path URL (/Patient/100000030009) disamarkan karena alasan yang sama. Pertahankan level logger di DEBUG hanya di mesin development — payload yang sudah disamarkan pun tetap membocorkan volume request dan jenis resource.
Token Caching
Secara default access token hidup di memori, per instance Oauth2. Di bawah PHP-FPM itu berarti satu request token per worker, dan setiap worker terus menanggung latensi tersebut sampai token kedaluwarsa.
FileTokenStore membagi satu token antar proses:
use agumil\SatuSehatSDK\Auth\FileTokenStore; use agumil\SatuSehatSDK\Auth\Oauth2; $oauth2 = new Oauth2([ 'token_store' => new FileTokenStore(__DIR__ . '/storage/satusehat-token.json'), ]);
Berkas ditulis dengan permission 0600 memakai pola temp-file-plus-rename yang atomik, sehingga pembaca tidak pernah melihat token setengah tertulis. Mode tersebut kemudian diverifikasi dengan fileperms(), karena chmod() melaporkan sukses bahkan pada filesystem yang tidak bisa menerapkan permission POSIX (FAT/vfat, sebagian network share); dalam kasus itu penulisan gagal dengan suara keras alih-alih meninggalkan kredensial yang bisa dibaca. Penyegaran berjalan di bawah flock eksklusif: ketika sepuluh worker mendapati token kedaluwarsa pada saat bersamaan, tepat satu yang memanggil SATUSEHAT dan sembilan lainnya memakai hasilnya.
Token yang disimpan ditandatangani HMAC dengan kunci client_secret, yang disuntikkan Oauth2 secara otomatis. Berkas yang ditulis pihak lain — misalnya token milik organisasi berbeda — gagal verifikasi dan diabaikan, sehingga data klinis tidak bisa dibelokkan ke organisasi yang salah hanya dengan menaruh berkas di disk. Berkas dari versi lama (tanpa tanda tangan) dianggap tidak ada dan diganti pada penyegaran berikutnya. Perlu dicatat: dua aplikasi dengan client_secret berbeda tidak bisa berbagi satu berkas token — masing-masing akan menolak token yang lain dan menyegarkan pada setiap panggilan, dan itulah sinyal yang memang diinginkan bahwa keduanya tidak seharusnya berbagi.
Catatan Windows Windows tidak menerapkan bit permission POSIX, jadi
chmod()di sana hanya mengubah flag read-only danfileperms()selalu melaporkan0666. SDK tidak berpura-pura sebaliknya: pemeriksaan mode dilewati di Windows, dan kerahasiaan bergantung pada ACL direktori yang Anda tunjuk. Di Windows, lindungi direktori itu dengan NTFS ACL. Pemeriksaan HMAC tetap berlaku di semua platform. Host PHP-FPM produksi tidak terpengaruh.
Arahkan path ke direktori yang bisa ditulis user PHP dan berada di luar web root. Berkasnya berisi kredensial hidup — perlakukan seperti secret lain dan jangan pernah di-commit.
Pengujian
composer install
composer test
Suite berjalan di PHPUnit dan tidak memerlukan akses jaringan: handler Guzzle di-mock, dan transport KYC/FHIR diganti recorder. Hasil pada repo ini (PHP 8.4, PHPUnit 11.5):
Tests: 43, Assertions: 78, Skipped: 2.
Dua test permission dilewati di Windows karena perilaku yang diuji (bit mode POSIX) tidak ada di sana; PosixFileTokenStore di tests/Support menutup jalur verifikasi tersebut di Windows.
CI berjalan di setiap push ke main dan setiap pull request, dengan 3 job (7 run) sesuai .github/workflows/ci.yml:
| Job | Yang dibuktikan |
|---|---|
test (matriks PHP 8.0 – 8.4) |
Suite lulus di setiap versi yang diklaim didukung, dan setiap berkas lolos php -l. |
lowest |
composer update --prefer-lowest tetap berjalan — batas versi di composer.json nyata, bukan harapan. |
audit |
composer audit tidak menemukan advisory pada dependensi yang terkunci. |
PHP 8.0 dan 8.1 memakai PHPUnit versi sendiri (9.6 dan 10.5) karena PHPUnit 11 yang terkunci membutuhkan PHP 8.2; PHP 8.0 menjalankan test yang sama lewat composer test:compat, yang melewati berkas konfigurasi yang tidak bisa dibaca PHPUnit 9.6.
Kredit
Perangkat lunak ini memakai paket open source berikut:
Lisensi
MIT License
Copyright (c) 2024 Agung Gumilang Copyright (c) 2026 Muchamad Desta Fadilah ~ mdestafadilah
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.