importkotlinx.coroutines.runBlockingimportorg.openlibx402.client.X402Clientimportorg.openlibx402.core.errors.X402Errorimportorg.openlibx402.core.models.PaymentAuthorizationimportorg.p2p.solanaj.core.AccountsuspendfunmanualPaymentExample(){// Load Solana account (securely in production!)valsecretKey=loadSecretKey()valaccount=Account(secretKey)// Create clientvalclient=X402Client(walletAccount=account,rpcUrl="https://api.devnet.solana.com",allowLocal=true// Development only)client.use{valurl="https://api.example.com/premium-data"try{// Make initial request (suspend function)valresponse=it.get(url)println("Success: ${response.body?.string()}")}catch(e:X402Error.PaymentRequired){println("Payment required!")// Get payment request detailsvalrequest=e.paymentRequestprintln("Amount: ${request.maxAmountRequired}")println("Asset: ${request.assetType}")println("Description: ${request.description}")// Create payment (suspend function)valauth=it.createPayment(request)println("Payment created: ${auth.signature}")// Retry request with payment authorizationvalretryResponse=it.get(url,auth)println("Success: ${retryResponse.body?.string()}")}}}funmain()=runBlocking{try{manualPaymentExample()}catch(e:X402Error){when(e){isX402Error.InsufficientFunds->{println("Insufficient funds!")println("Required: ${e.requiredAmount}")println("Available: ${e.availableAmount}")}else->{println("Error: ${e.message}")}}}catch(e:Exception){e.printStackTrace()}}privatefunloadSecretKey():ByteArray{// In production, load from secure storagevalkeyEnv=System.getenv("SOLANA_SECRET_KEY")if(keyEnv!=null){returnjava.util.Base64.getDecoder().decode(keyEnv)}// For demo: generate random accountprintln("Warning: Using random account for demo")returnAccount().secretKey}
importkotlinx.coroutines.runBlockingimportorg.openlibx402.client.X402AutoClientimportorg.openlibx402.core.errors.X402Errorimportorg.p2p.solanaj.core.AccountsuspendfunautoPaymentExample(){valsecretKey=loadSecretKey()valaccount=Account(secretKey)// Build auto-client with DSLvalclient=X402AutoClient(account){rpcUrl="https://api.devnet.solana.com"maxPaymentAmount="1.0"// Max 1 USDC per requestmaxRetries=2// Retry up to 2 timesallowLocal=true// Development only}client.use{// Client automatically handles 402 and retriesvalurl="https://api.example.com/premium-data"valresponse=it.get(url)println("Success: ${response.body?.string()}")}}funmain()=runBlocking{try{autoPaymentExample()}catch(e:X402Error.InsufficientFunds){println("Insufficient funds for payment")}catch(e:X402Error.PaymentRequired){println("Payment required but max retries exceeded")}catch(e:X402Error){println("Error: ${e.message}")}catch(e:Exception){e.printStackTrace()}}privatefunloadSecretKey():ByteArray{valkeyEnv=System.getenv("SOLANA_SECRET_KEY")returnkeyEnv?.let{java.util.Base64.getDecoder().decode(it)}?:Account().secretKey}
importkotlinx.coroutines.runBlockingimportkotlinx.serialization.Serializableimportkotlinx.serialization.encodeToStringimportkotlinx.serialization.json.Jsonimportorg.openlibx402.client.X402Clientimportorg.p2p.solanaj.core.Account@SerializabledataclassQueryRequest(valquery:String,valoptions:Map<String,String>=emptyMap())suspendfunpostRequestExample(){valaccount=Account(loadSecretKey())valclient=X402Client(account,allowLocal=true)client.use{valurl="https://api.example.com/process"// Create request with kotlinx.serializationvalrequestData=QueryRequest(query="Analyze this data",options=mapOf("format"to"json"))valjsonBody=Json.encodeToString(requestData)try{// Make POST request (suspend function)valresponse=it.post(url,jsonBody)println("Result: ${response.body?.string()}")}catch(e:X402Error.PaymentRequired){// Handle paymentvalauth=it.createPayment(e.paymentRequest)// Retry with paymentvalretryResponse=it.post(url,jsonBody,auth)println("Result: ${retryResponse.body?.string()}")}}}funmain()=runBlocking{try{postRequestExample()}catch(e:Exception){e.printStackTrace()}}privatefunloadSecretKey()=Account().secretKey
Example 4: Type-Safe Error Handling with When Expressions¶
Comprehensive error handling using sealed classes with exhaustive when expressions.
importkotlinx.coroutines.runBlockingimportokhttp3.OkHttpClientimportorg.openlibx402.client.X402AutoClientimportorg.p2p.solanaj.core.Accountimportjava.util.concurrent.TimeUnitsuspendfuncustomHttpExample(){valaccount=Account(loadSecretKey())// Custom HTTP client with longer timeoutsvalhttpClient=OkHttpClient.Builder().connectTimeout(60,TimeUnit.SECONDS).readTimeout(120,TimeUnit.SECONDS).writeTimeout(60,TimeUnit.SECONDS).retryOnConnectionFailure(true).build()// Use custom client with DSLvalclient=X402AutoClient(account){rpcUrl="https://api.mainnet-beta.solana.com"maxPaymentAmount="5.0"this.httpClient=httpClient}client.use{valresponse=it.get("https://api.example.com/slow-endpoint")println("Success: ${response.body?.string()}")}}funmain()=runBlocking{try{customHttpExample()}catch(e:Exception){e.printStackTrace()}}privatefunloadSecretKey()=Account().secretKey
importkotlinx.coroutines.*importorg.openlibx402.client.X402Clientimportorg.openlibx402.core.errors.X402Errorimportorg.p2p.solanaj.core.AccountsuspendfunstructuredConcurrencyExample()=coroutineScope{valaccount=Account(loadSecretKey())valclient=X402Client(account,allowLocal=true)client.use{try{// All child coroutines are cancelled if any failsvalresponse=it.get("https://api.example.com/data")// Process in parallel with structured concurrencyvaljob1=launch(Dispatchers.IO){processData(response.body?.string()?:"")}valjob2=launch(Dispatchers.IO){saveToDatabase(response.body?.string()?:"")}// Wait for both to completejob1.join()job2.join()println("All processing complete")}catch(e:X402Error){// All child coroutines are automatically cancelledprintln("Error: ${e.message}")// Cancel parent scope if neededthrowe}}}privatesuspendfunprocessData(data:String)=withContext(Dispatchers.Default){println("Processing data: ${data.take(50)}")delay(1000)println("Processing complete")}privatesuspendfunsaveToDatabase(data:String)=withContext(Dispatchers.IO){println("Saving to database")delay(1500)println("Save complete")}funmain()=runBlocking{try{structuredConcurrencyExample()}catch(e:Exception){println("Fatal error: ${e.message}")}}privatefunloadSecretKey()=Account().secretKey
importkotlinx.coroutines.*importkotlinx.coroutines.flow.*importorg.openlibx402.client.X402AutoClientimportorg.openlibx402.core.errors.X402Errorimportorg.p2p.solanaj.core.AccountsuspendfunstreamingRequestsExample(){valaccount=Account(loadSecretKey())valclient=X402AutoClient(account){rpcUrl="https://api.devnet.solana.com"maxPaymentAmount="10.0"}client.use{// Create a flow of URLs to processvalurls=flowOf("https://api.example.com/stream/1","https://api.example.com/stream/2","https://api.example.com/stream/3","https://api.example.com/stream/4")// Process each URL with payment handlingurls.map{url->try{valresponse=it.get(url)Result.success(urltoresponse.body?.string())}catch(e:X402Error){Result.failure<Pair<String,String?>>(e)}}.collect{result->result.onSuccess{(url,data)->println("$url: $data")}.onFailure{e->println("Failed: ${e.message}")}}}}funmain()=runBlocking{streamingRequestsExample()}privatefunloadSecretKey()=Account().secretKey
Example 10: Data Class Features and Extension Functions¶
Leveraging Kotlin data classes and extension functions.
importkotlinx.datetime.Clockimportkotlinx.datetime.Instantimportorg.openlibx402.core.models.PaymentRequestimportkotlin.time.Duration.Companion.minutes// Extension function for PaymentRequestfunPaymentRequest.isAffordable(balance:Double):Boolean=balance>=maxAmountRequired.toDouble()// Extension function to check if expiring soonfunPaymentRequest.isExpiringSoon(threshold:kotlin.time.Duration=5.minutes):Boolean{valnow=Clock.System.now()valtimeUntilExpiry=expiresAt-nowreturntimeUntilExpiry<threshold}fundataClassExample(){// Create payment request with data classvalrequest=PaymentRequest(maxAmountRequired="0.10",assetType="SPL",assetAddress="EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",paymentAddress="7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU",network="solana-devnet",expiresAt=Clock.System.now()+10.minutes,nonce="unique-nonce",paymentId="pay_123",resource="/api/data",description="Premium data access")// Data class destructuringval(amount,assetType,address)=requestprintln("Amount: $amount, Asset: $assetType, Address: $address")// Immutable copy with changesvalupdated=request.copy(maxAmountRequired="0.20")println("Updated amount: ${updated.maxAmountRequired}")// Extension functionsvalbalance=1.0println("Is affordable: ${request.isAffordable(balance)}")println("Expiring soon: ${request.isExpiringSoon()}")// Serialize to JSONvaljson=request.toJson()println("JSON: $json")// Deserialize from JSONvalparsed=PaymentRequest.fromJson(json)println("Parsed equals original: ${parsed==request}")}funmain(){dataClassExample()}