Need peer-to-peer encryption?
├─ Yes → Use DIDComm
└─ No
├─ Web-based OAuth integration?
│ ├─ Yes → Use OIDC4VCI
│ └─ No
│ └─ Browser-based wallet?
│ ├─ Yes → Use CHAPI
│ └─ No → Use DIDComm (default)
// Store offer ID for later referencevalofferId=offer.offerId// Extract protocol-specific data if neededvalofferData=offer.offerDatawhen(offer.protocolName){"didcomm"->{valdidCommMessage=offerDataasDidCommMessage// Handle DIDComm-specific data}"oidc4vci"->{valoidcOffer=offerDataasOidc4VciOffer// Handle OIDC4VCI-specific data}}
Step 5: Request Credential
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
valrequestResult=exchangeService.request(ExchangeRequest.Request(protocolName="didcomm".requireExchangeProtocolName(),holderDid=holderDid,issuerDid=issuerDid,offerId=offerId,// Reference to the offeroptions=ExchangeOptions.builder().addMetadata("fromKeyId","$holderDid#key-1").addMetadata("toKeyId","$issuerDid#key-1").build()))valrequest=when(requestResult){isExchangeResult.Success->requestResult.valueelse->throwIllegalStateException("Request failed: $requestResult")}println("✅ Request created: ${request.requestId}")
importorg.trustweave.credential.model.vc.VerifiableCredentialimportorg.trustweave.credential.model.vc.CredentialSubjectimportorg.trustweave.credential.model.vc.Issuerimportorg.trustweave.credential.model.CredentialTypeimportorg.trustweave.core.identifiers.Iriimportkotlinx.serialization.json.JsonPrimitiveimportkotlinx.datetime.Clock// Create the credentialvalcredential=VerifiableCredential(type=listOf(CredentialType.fromString("VerifiableCredential"),CredentialType.fromString("PersonCredential")),issuer=Issuer.IriIssuer(Iri(issuerDid.value)),issuanceDate=Clock.System.now(),credentialSubject=CredentialSubject(id=holderDid,claims=mapOf("name"toJsonPrimitive("Alice"),"email"toJsonPrimitive("alice@example.com"),"role"toJsonPrimitive("Developer"))))// Issue the credentialvalissueResult=exchangeService.issue(ExchangeRequest.Issue(protocolName="didcomm".requireExchangeProtocolName(),issuerDid=issuerDid,holderDid=holderDid,credential=credential,requestId=request.requestId,// Reference to the requestoptions=ExchangeOptions.builder().addMetadata("fromKeyId","$issuerDid#key-1").addMetadata("toKeyId","$holderDid#key-1").build()))valissue=when(issueResult){isExchangeResult.Success->issueResult.valueelse->throwIllegalStateException("Issue failed: $issueResult")}println("✅ Credential issued:")println(" Credential ID: ${issue.credential.id}")println(" Issue ID: ${issue.issueId}")
What happens:
Registry validates request ID exists
Protocol creates issue message with credential
Credential is signed and encrypted
Returns issued credential with proof
Step 7: Verify Credential
1
2
3
4
5
6
7
8
9
10
// The issued credential can now be verifiedvalverification=trustLayer.verify{credential(issue.credential)}if(verification.valid){println("✅ Credential is valid")}else{println("❌ Credential invalid: ${verification.errors}")}
Requesting a Proof
Complete workflow for requesting and receiving a proof presentation.
// Prover creates a verifiable presentationvalpresentation=VerifiablePresentation(type=listOf("VerifiablePresentation"),holder=proverDid,verifiableCredential=listOf(credential),// Credential from previous workflowproof=proof// Proof of presentation)
valpresentationResult=exchangeService.presentProof(ProofExchangeRequest.Presentation(protocolName="didcomm".requireExchangeProtocolName(),proverDid=proverDid,verifierDid=verifierDid,presentation=presentation,requestId=proofRequest.requestId,// Reference to the requestoptions=ExchangeOptions.builder().addMetadata("fromKeyId","$proverDid#key-1").addMetadata("toKeyId","$verifierDid#key-1").build()))valpresentationResponse=when(presentationResult){isExchangeResult.Success->presentationResult.valueelse->throwIllegalStateException("Presentation failed: $presentationResult")})println("✅ Proof presented: ${presentationResponse.presentationId}")
Step 4: Verify Presentation
1
2
3
4
5
6
7
8
9
10
11
12
valverification=trustLayer.verify{presentation(presentationResponse.presentation)}if(verification.valid){println("✅ Presentation is valid")// Extract attributesvalname=extractAttribute(presentationResponse.presentation,"name")println("Name: $name")}else{println("❌ Presentation invalid: ${verification.errors}")}
Protocol Selection
Guide for choosing the right protocol for your use case.
// Browser wallet interactionvalofferResult=exchangeService.offer(ExchangeRequest.Offer(protocolName="chapi".requireExchangeProtocolName(),issuerDid=issuerDid,holderDid=holderDid,credentialPreview=preview,options=ExchangeOptions.Empty))valoffer=when(offerResult){isExchangeResult.Success->offerResult.valueelse->throwIllegalStateException("Offer failed: $offerResult")}// Use offer.chapiMessage with navigator.credentials.store()
Error Recovery
Strategies for handling errors and recovering from failures.
funvalidateRequest(request:CredentialOfferRequest):ValidationResult{valerrors=mutableListOf<String>()// Validate DIDsif(!isValidDid(request.issuerDid)){errors.add("Invalid issuer DID: ${request.issuerDid}")}if(!isValidDid(request.holderDid)){errors.add("Invalid holder DID: ${request.holderDid}")}// Validate previewif(request.credentialPreview.attributes.isEmpty()){errors.add("Credential preview must have at least one attribute")}// Validate protocol-specific options// (Implementation depends on protocol)returnif(errors.isEmpty()){ValidationResult.Valid}else{ValidationResult.Invalid(errors)}}// Use before operation (validate DIDs, preview, etc. before calling exchangeService)// Note: The new API uses Did types and ExchangeRequest types which provide better type safetyvalvalidation=validateRequest(issuerDid,holderDid,credentialPreview)if(validationisValidationResult.Invalid){println("Validation failed:")validation.errors.forEach{println(" - $it")}return}valofferResult=exchangeService.offer(ExchangeRequest.Offer(protocolName="didcomm".requireExchangeProtocolName(),issuerDid=issuerDid,holderDid=holderDid,credentialPreview=credentialPreview,options=ExchangeOptions.builder().build()))valoffer=when(offerResult){isExchangeResult.Success->offerResult.valueelse->throwIllegalStateException("Offer failed: $offerResult")}