Skip to content

Kotlin / JVM SDK

Kotlin   JVM 17+   Android

Coroutines-native SDK built on Ktor HTTP client. Works in Android apps, Spring Boot backends, and any JVM project.

Install

kotlin
// build.gradle.kts
dependencies {
    implementation("ly.neptune.astro:astro-kotlin:1.0.0")
    // Or from local path during development:
    // implementation(project(":packages:astro-kotlin"))
}

For the git dependency before publishing:

kotlin
// settings.gradle.kts
sourceControl {
    gitRepository(URI("https://github.com/neptune-astro/neptune-astro.git")) {
        producesModule("ly.neptune.astro:astro-kotlin")
    }
}

Quick Start

kotlin
import ly.neptune.astro.*
import ly.neptune.astro.models.*
import kotlinx.coroutines.runBlocking

val astro = astroClient {
    baseUrl = "https://astro.neptune.ly/api/v1"
    merchantKey = System.getenv("ASTRO_MERCHANT_KEY")
}

runBlocking {
    val session = astro.payments.createSession(
        CreateSessionRequest(
            amount = 50_000L,    // 50.000 LYD
            currency = "LYD",
            destination = Destination(type = "alias", value = "mtellesy"),
            reference = "order_1042",
            redirectUrl = "https://myapp.com/checkout/result"
        )
    )
    println("Checkout: ${session.checkoutUrl}")

    // Resolve alias
    val resolved = astro.identity.resolve("mtellesy")
    println("Bank: ${resolved.bankHandle}")
}

astro.close() // closes Ktor HttpClient

Spring Boot Integration

kotlin
@Configuration
class AstroConfig {
    @Bean
    fun astroClient(): AstroClient = astroClient {
        baseUrl = System.getenv("ASTRO_BASE_URL")
        merchantKey = System.getenv("ASTRO_MERCHANT_KEY")
    }
}

@RestController
@RequestMapping("/api/checkout")
class CheckoutController(private val astro: AstroClient) {

    @PostMapping
    suspend fun createSession(@RequestBody req: OrderRequest): ResponseEntity<Map<String, String>> {
        val session = astro.payments.createSession(
            CreateSessionRequest(
                amount = req.amount,
                currency = "LYD",
                destination = Destination("alias", req.customerAlias),
                reference = req.orderId
            )
        )
        return ResponseEntity.ok(mapOf("checkout_url" to session.checkoutUrl, "session_id" to session.sessionId))
    }
}

Payments

kotlin
// Get session
val session = astro.payments.getSession("ops_01HZGV...")
println(session.status) // PENDING | PROCESSING | COMPLETED | FAILED

// List sessions
val result = astro.payments.listSessions(status = "COMPLETED")

// Cancel
astro.payments.cancelSession(session.sessionId)

// Mandate
val mandate = astro.payments.createMandate(
    CreateMandateRequest(
        customerAlias = "mtellesy",
        amount = 5_000L,
        currency = "LYD",
        interval = "monthly",
        description = "Monthly subscription",
        startDate = "2026-05-01"
    )
)

Open Banking

kotlin
// Create consent
val consent = astro.openBanking.createConsent(
    CreateConsentRequest(
        bankHandle = "andalus",
        scopes = listOf("accounts:read", "transactions:read"),
        redirectUri = "https://myapp.com/ob/callback",
        state = UUID.randomUUID().toString(),
        codeChallenge = pkce.challenge
    )
)
// Redirect user to consent.consentUrl

// Exchange code
val tokens = astro.openBanking.exchangeCode(
    code = callbackParams["code"]!!,
    redirectUri = "https://myapp.com/ob/callback",
    consentId = consent.consentId,
    codeVerifier = pkce.verifier
)

// Fetch data
val accounts = astro.openBanking.getAccounts(tokens.accessToken, consent.consentId)
val txns = astro.openBanking.getTransactions(tokens.accessToken, consent.consentId, accounts[0].accountId)

Webhook Handling

kotlin
import ly.neptune.astro.webhooks.WebhookReceiver
import kotlinx.serialization.json.*

val receiver = WebhookReceiver(secret = System.getenv("ASTRO_WEBHOOK_SECRET"))

receiver
    .on("payment.completed") { payload ->
        val obj = payload as? JsonObject ?: return@on
        val ref = obj["data"]?.jsonObject?.get("reference")?.jsonPrimitive?.content
        db.markPaid(ref)
    }
    .on("payment.failed") { payload ->
        // handle failure
    }

// Ktor route
post("/webhooks/astro") {
    val body = call.receiveText()
    val sig = call.request.headers["X-OpenWave-Signature"]
        ?: return@post call.respond(HttpStatusCode.BadRequest)

    try {
        receiver.handle(body, sig)
        call.respond(HttpStatusCode.OK)
    } catch (e: IllegalArgumentException) {
        call.respond(HttpStatusCode.Unauthorized)
    }
}

Error Handling

kotlin
try {
    val session = astro.payments.createSession(...)
} catch (e: AstroRequestException) {
    when (e.code) {
        "INSUFFICIENT_FUNDS" -> handleInsufficientFunds()
        "ALIAS_NOT_FOUND"    -> handleAliasNotFound()
        else -> logger.error("Astro error [${e.code}] ${e.message} (req: ${e.requestId})")
    }
}

Source

packages/astro-kotlin/ in the neptune-astro repo.

Built on the OpenWave open standard.