Search by

kayya / api-sdk

bithousebali

Integrasi server tenant dengan marketplace Kayya API: verifikasi panggilan gateway, tanda tangan webhook & sinkronisasi.

v0.1.0 2026-09-28 11:26 UTC

This package is auto-updated.

Last update: 2026-09-28 03:51:09 UTC


README

Menghubungkan server API kamu dengan marketplace Kayya API (kontrak integrasi tenant v2). PHP 8.1+, tanpa dependency wajib.

Yang wajib hanya satu: memastikan setiap panggilan benar datang dari gateway Kayya API. Tanpa itu, base URL kamu adalah API terbuka bagi siapa pun yang menemukannya. Sisanya opsional:

FungsiWajib?Untuk
Verifikasi panggilan gatewayYaMenolak panggilan yang tidak lewat gateway (401), menyediakan identitas buyer
Penerima webhookTidakDiberi tahu saat entitlement dibeli, diperpanjang, atau dicabut
Penyimpan lokalTidakDaftar pelanggan di database kamu sendiri
SinkronisasiTidakJaring pengaman webhook; membangun ulang data lokal dari nol
kayya-api doctor—Membuktikan integrasi siap sebelum go-live

Panduan lengkap kontraknya ada di halaman Integrasi untuk developer di situs Kayya API (/developer/integrasi). SDK ini tidak wajib — yang diuji marketplace adalah perilaku server kamu — tetapi ia menjadikan jalur yang aman sekaligus jalur yang paling mudah.

Instalasi

composer require kayya/api-sdk

Konfigurasi

Variabel lingkunganIsiDibutuhkan untuk
KAYYA_API_SECRETSecret integrasi — dashboard tenant → Integrasi. Pakai apa adanya, termasuk awalan whsec_Semua
KAYYA_API_TENANT_IDTenant ID dari halaman yang samaSinkronisasi
KAYYA_API_BASE_URLAlamat API marketplace yang diberikan saat onboarding (dengan atau tanpa /api/v1)Sinkronisasi

Setiap nilai juga bisa diberikan sebagai argumen, yang selalu menang atas variabel lingkungan. Secret yang tidak diisi membuat middleware melempar KonfigurasiSalah saat dibuat — lebih aman daripada diam-diam menerima setiap panggilan.

Mulai cepat

Middleware dipasang di depan seluruh route, bukan per route atau per grup. Uji integrasi marketplace mengetuk satu path acak dengan tanda tangan palsu dan menuntut 401; verifikasi yang hanya dipasang di sebagian route gagal di sana.

Laravel 12+

// bootstrap/app.php
->withMiddleware(function (Middleware $middleware): void {
    $middleware->prepend(\Kayya\Api\Laravel\KayyaMiddleware::class);
})
// routes/api.php — identitas buyer disuntikkan container
use Kayya\Api\Pemanggil;

Route::get('/v1/cuaca', fn (Pemanggil $p) => ['untuk' => $p->entitlementId]);

Isi KAYYA_API_SECRET di .env. Opsi lain di config/kayya.php:

php artisan vendor:publish --tag=kayya-config

prepend, bukan append dan bukan grup api: middleware grup tidak pernah berjalan untuk path yang tidak punya route. Health check bawaan Laravel (/up) ikut diverifikasi — kalau load balancer kamu memakainya, masukkan ke kecualikan.

Slim 4 & framework PSR-15 lain

use Kayya\Api\Gerbang;
use Kayya\Api\Psr15\KayyaMiddleware;

// Slim menjalankan middleware dari yang terakhir ditambahkan: tambahkan PALING AKHIR.
$app->add(new KayyaMiddleware($app->getResponseFactory(), new Gerbang(kecualikan: ['/healthz'])));

$app->get('/v1/cuaca', function (Request $request, Response $response) {
    $p = KayyaMiddleware::pemanggil($request);
    // ...
});

Butuh psr/http-server-middleware (sudah terpasang bersama Slim, Mezzio, dan framework PSR-15 lain). Pasang sebagai middleware terluar — sebelum routing, dan sebelum middleware yang mengganti method dari _method: gateway menandatangani method di baris permintaan.

PHP polos, CodeIgniter, & lainnya

require __DIR__ . '/vendor/autoload.php';

// Paling awal, sebelum routing.
$pemanggil = \Kayya\Api\Polos::lindungi();

Panggilan yang tidak sah dijawab 401 dan skrip berhenti di situ. Opsi: Polos::lindungi(new Gerbang(kecualikan: ['/healthz'])).

Identitas pemanggil

PropertiIsi
buyerIdUUID buyer di marketplace
entitlementIdUUID entitlement — kunci yang sama dengan webhook & sinkronisasi
produkSlug produk yang dipanggil
requestIdSertakan saat menghubungi dukungan — ditelusuri di sisi marketplace juga
expiresAtDateTimeImmutable (UTC) akhir periode berjalan — informasi saja; null kalau tak terurai
ujiIntegrasitrue untuk panggilan uji integrasi marketplace — jangan dicatat sebagai pemakaian

Jangan menolak panggilan berdasarkan expiresAt. Keputusan akses sepenuhnya milik gateway: ada keadaan di mana akses tetap sah meski tanggalnya lewat, dan panggilan yang sampai ke server kamu sudah diputuskan sah.

Kamu tidak pernah menerima API key buyer, dan tidak membutuhkannya.

Opsi

new Gerbang(...) untuk PSR-15 & PHP polos; kunci config/kayya.php untuk Laravel.

Gerbangconfig/kayya.phpBawaanIsi
secretsecretKAYYA_API_SECRETSecret integrasi
prefixYangDibuangprefix_yang_dibuang''Awalan path yang dibuang reverse proxy di depan server kamu (proxy menerima /v1/cuaca, meneruskan /cuaca → isi '/v1')
kecualikankecualikan[]Path yang tidak diverifikasi, dibandingkan persis (tanpa query) — mis. ['/healthz']. Path di sini terbuka untuk siapa pun; jangan pernah memasukkan route API
periksaPathperiksa_pathtrueMatikan hanya kalau proxy menulis ulang path dengan cara yang tidak bisa dijelaskan satu awalan
webhookwebhooknull / falseTerima webhook di path webhook (lihat Webhook)
pathWebhookpath_webhook'/kayya/webhook'
logger— (logger Laravel)nullLogger PSR-3 untuk mencatat panggilan yang ditolak

Jawaban yang ditolak

401 dengan body JSON:

{"alasan": "timestamp", "keterangan": "X-Marketplace-Timestamp 412 detik di belakang jam server ini ..."}
alasanPenyebab paling mungkin
header_kurangBukan panggilan gateway (pemindai internet, health check)
timestampJam server kamu meleset lebih dari 5 menit — nyalakan NTP
tanda_tanganSecret berbeda: awalan whsec_ terpotong, atau secret baru belum dipasang setelah diganti
pathReverse proxy membuang awalan path — isi prefixYangDibuang

Dashboard tenant membaca alasan untuk menampilkan penyebab saat uji integrasi gagal. Penolakan dicatat lewat logger (warning; header_kurang di debug supaya pemindai internet tidak memenuhi log).

Webhook

Laravel — nyalakan di config/kayya.php, lalu dengarkan event-nya:

'webhook' => true,
'simpan_entitlement' => true,   // lihat "Penyimpan lokal"
use Kayya\Api\Laravel\EntitlementBerubah;

Event::listen(function (EntitlementBerubah $e) {
    if ($e->perubahan->keputusan?->value === 'baru') {
        // email sambutan untuk $e->perubahan->entitlement->buyerId
    }
});

PSR-15 & PHP polos — pasang penerimanya di Gerbang:

use Kayya\Api\{Gerbang, PenerimaWebhook, Perubahan};

$gerbang = new Gerbang(webhook: new PenerimaWebhook(
    penyimpan: $penyimpan,
    saatPerubahan: function (Perubahan $p): void { /* ... */ },
));

Isi URL webhook di dashboard tenant → Integrasi dengan https://<server kamu>/kayya/webhook.

  • Kiriman webhook dijawab middleware sebelum routing, CSRF, dan body-parser — tanda tangan dihitung dari raw body.
  • ping dan event yang belum dikenal versi SDK ini dijawab 2xx.
  • Dengan penyimpan, entitlement.updated diterapkan dengan aturan updated_at dan callback/event hanya dipanggil kalau data lokal berubah — keputusan salah satu baru, perpanjangan, dicabut, diperbarui. Duplikat dan retry lama tidak memanggilnya lagi.
  • Tanpa penyimpan, callback dipanggil untuk setiap kiriman sah (keputusan = null), dan urutannya urusanmu.
  • Callback berjalan setelah data tersimpan, paling banyak sekali per perubahan. Galat di dalamnya dicatat, tidak menggagalkan kiriman: jawaban 5xx membuat marketplace mengulang, dan enam kegagalan berturut menonaktifkan webhook kamu. Pekerjaan berat atau yang tidak boleh hilang: listener ShouldQueue (Laravel) atau antrean yang tahan lama.
  • Marketplace menunggu paling lama 10 detik.

Webhook adalah pemberitahuan, bukan penjaga akses: entitlement yang dicabut sudah ditolak gateway detik itu juga.

Penyimpan lokal

AdapterPemasangan
Kayya\Api\Penyimpan\PenyimpanMemoriTest & percobaan — hilang di akhir request
Kayya\Api\Penyimpan\PenyimpanPdo($pdo)SQLite, PostgreSQL, MySQL/MariaDB; buatTabel: true, atau jalankan DDL dari PenyimpanPdo::skema($driver) di migrasi kamu
Kayya\Api\Laravel\PenyimpanLaravelphp artisan vendor:publish --tag=kayya-migrations && php artisan migrate, lalu 'simpan_entitlement' => true

Semua adapter memakai tabel yang sama — kayya_entitlement (kunci entitlement_id) dan kayya_penanda_sync — dengan waktu UTC berpresisi mikrodetik (DATETIME(6) di MySQL/MariaDB). Satu aturan berlaku untuk webhook maupun sinkronisasi: per entitlement_id, hanya kalau updated_at yang datang lebih baru. Perpanjangan memajukan expiresAt; retry lama yang tiba terlambat tidak pernah menimpa data baru, juga saat webhook dan sinkronisasi menulis baris yang sama bersamaan.

Entitlement::masihBerlaku() menghitung dari status + expiresAt data lokal — untuk ditampilkan, bukan untuk memutuskan akses. limitHit disimpan tapi tidak ditegakkan; kuota ditegakkan gateway.

Adapter sendiri cukup mengimplementasikan lima metode Kayya\Api\Penyimpan\Penyimpan (Laravel: ikat ke container dengan $this->app->bind(Penyimpan::class, ...)).

Sinkronisasi

Laravel — isi KAYYA_API_TENANT_ID & KAYYA_API_BASE_URL, lalu jadwalkan:

// routes/console.php
Schedule::command('kayya:sinkron')->hourly()->withoutOverlapping();

php artisan kayya:sinkron menjalankan satu putaran; --terus menjalankannya sebagai proses tersendiri (sekarang, lalu tiap jam). Setiap perubahan juga disiarkan sebagai event EntitlementBerubah.

PHP polos — satu putaran dari cron tiap jam:

use Kayya\Api\Penyinkron;
use Kayya\Api\Penyimpan\PenyimpanPdo;

(new Penyinkron(new PenyimpanPdo(new PDO($dsn, $pengguna, $sandi))))->jalankanSekali();

atau proses tersendiri: (new PenjadwalSync(new Penyinkron($penyimpan)))->jalankan().

  • Halaman pertama ?since=, berikutnya ?kursor= apa adanya; penanda (sampai) disimpan hanya setelah halaman terakhir. Putaran yang gagal di tengah diulang dari penanda lama.
  • Kegagalan (jaringan, 401, 5xx) dilempar sebagai GalatSync dengan saran penyebabnya; PenjadwalSync mencatatnya lalu mengulang bertahap 1, 5, 15, 30 menit — buyer tetap dilayani tanpa data lokal sama sekali.
  • Jalankan di satu proses. Beberapa proses yang masing-masing menyinkron menarik data yang sama berkali-kali.
  • Marketplace membatasi 60 permintaan per menit per tenant. Lewat dari itu dijawab 429 (GalatSync->status === 429); penjadwal mundur 1 menit lalu mengulang putaran dari penanda lama.
  • Pengalihan tidak diikuti — tanda tangan melekat pada alamat.

kayya-api doctor

vendor/bin/kayya-api doctor                                           # konfigurasi, test vector, sinkronisasi, jam
vendor/bin/kayya-api doctor --url https://api.contoh.id --path /v1/ping
vendor/bin/kayya-api doctor --webhook https://api.contoh.id/kayya/webhook
php artisan kayya:doctor --url https://api.contoh.id                  # Laravel: konfigurasi dari config/kayya.php

Memeriksa konfigurasi, menjalankan seluruh test vector resmi terhadap SDK yang terpasang, mengirim satu permintaan sinkronisasi (membuktikan tenant ID & secret), menghitung selisih jam dengan marketplace, lalu — kalau --url diberikan — mengetuk server kamu seperti uji integrasi: tanda tangan sah harus dilayani, tanda tangan palsu ke akar dan ke path acak harus 401. Keluar dengan status 1 kalau ada yang gagal. Secret tidak pernah dicetak.

Menguji aplikasimu

Kayya\Api\Uji membuat panggilan gateway dan kiriman webhook bertanda tangan dengan secret uji, tanpa marketplace. Dengan KAYYA_API_SECRET=whsec_uji di lingkungan test (mis. <env> di phpunit.xml):

use Kayya\Api\Uji;

public function test_cuaca(): void
{
    $this->get('/v1/cuaca', Uji::headerGateway('/v1/cuaca', secret: 'whsec_uji'))->assertOk();
}

public function test_palsu(): void
{
    $this->get('/v1/cuaca', Uji::headerGateway('/v1/cuaca', secret: 'whsec_lain'))->assertUnauthorized();
}

Uji::kirimanWebhook($payload, secret: ...) mengembalikan [header, rawBody] untuk menguji penerima webhook.

Kompatibilitas

PHP8.1 – 8.5
Kontrak integrasiv2
Laravel12 & 13 (Laravel 10 & 11 sudah EOL)
PSR-15psr/http-server-middleware 1.x; diuji dengan Slim 4
DatabaseSQLite, PostgreSQL, MySQL/MariaDB (PDO & Laravel)

Setiap perubahan diuji terhadap test vector resmi, penyimpan diuji di PostgreSQL & MySQL sungguhan, dan aplikasi contoh (PHP polos, Slim, Laravel) diuji di server sungguhan terhadap simulator marketplace.

Menemukan celah keamanan? Lihat SECURITY.md — jangan lewat issue publik.