From a3b577e2c25fd99286dfc57a4aff84bebd636e50 Mon Sep 17 00:00:00 2001 From: Trent Larson Date: Sun, 26 Mar 2023 09:04:00 -0600 Subject: [PATCH] allow to confirm an amount received --- src/libs/endorserServer.ts | 7 ++ src/views/ContactAmountsView.vue | 120 ++++++++++++++++++++++++++++--- src/views/ContactsView.vue | 1 + 3 files changed, 119 insertions(+), 9 deletions(-) diff --git a/src/libs/endorserServer.ts b/src/libs/endorserServer.ts index ee15d2a5..8d3cc765 100644 --- a/src/libs/endorserServer.ts +++ b/src/libs/endorserServer.ts @@ -1,5 +1,12 @@ +export const SCHEMA_ORG_CONTEXT = "https://schema.org"; export const SERVICE_ID = "endorser.ch"; +export interface AgreeVerifiableCredential { + "@context": string; + "@type": string; + object: Record; +} + export interface GiveServerRecord { agentDid: string; amount: number; diff --git a/src/views/ContactAmountsView.vue b/src/views/ContactAmountsView.vue index e0defdd8..da8577cf 100644 --- a/src/views/ContactAmountsView.vue +++ b/src/views/ContactAmountsView.vue @@ -72,10 +72,10 @@ Confirmed - +
{{ record.description }} @@ -98,10 +98,10 @@ Confirmed
- +
{{ record.description }} @@ -109,6 +109,17 @@ + +
+ +

{{ alertTitle }}

+

{{ alertMessage }}

+
@@ -120,11 +131,20 @@ import { accountsDB, db } from "@/db"; import { Contact } from "@/db/tables/contacts"; import { MASTER_SETTINGS_KEY } from "@/db/tables/settings"; import { AppString } from "@/constants/app"; -import { accessToken } from "@/libs/crypto"; -import { GiveServerRecord } from "@/libs/endorserServer"; +import { accessToken, SimpleSigner } from "@/libs/crypto"; +import { + AgreeVerifiableCredential, + GiveServerRecord, + RegisterVerifiableCredential, + SCHEMA_ORG_CONTEXT, + SERVICE_ID, +} from "@/libs/endorserServer"; +import * as didJwt from "did-jwt"; +import { AxiosError } from "axios"; @Options({}) export default class ContactsView extends Vue { + activeDid = ""; contact: Contact | null = null; giveRecords: Array = []; @@ -135,10 +155,10 @@ export default class ContactsView extends Vue { this.contact = (await db.contacts.get(contactDid)) || null; const settings = await db.settings.get(MASTER_SETTINGS_KEY); - const activeDid = settings?.activeDid; + this.activeDid = settings?.activeDid || ""; - if (activeDid && this.contact) { - this.loadGives(activeDid, this.contact); + if (this.activeDid && this.contact) { + this.loadGives(this.activeDid, this.contact); } } @@ -220,6 +240,88 @@ export default class ContactsView extends Vue { } } + async confirm(record: GiveServerRecord) { + // Make claim + // I use clone here because otherwise it gets a Proxy object. + const origClaim: Record = R.clone(record.fullClaim); + if (record.fullClaim["@context"] == SCHEMA_ORG_CONTEXT) { + delete origClaim["@context"]; + } + origClaim["identifier"] = record.handleId; + const vcClaim: AgreeVerifiableCredential = { + "@context": SCHEMA_ORG_CONTEXT, + "@type": "AgreeAction", + object: origClaim, + }; + + // Make a payload for the claim + const vcPayload = { + vc: { + "@context": ["https://www.w3.org/2018/credentials/v1"], + type: ["VerifiableCredential"], + credentialSubject: vcClaim, + }, + }; + + // Create a signature using private key of identity + await accountsDB.open(); + const accounts = await accountsDB.accounts.toArray(); + const account = R.find((acc) => acc.did === this.activeDid, accounts); + const identity = JSON.parse(account?.identity || "undefined"); + if (identity.keys[0].privateKeyHex !== null) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const privateKeyHex: string = identity.keys[0].privateKeyHex!; + const signer = await SimpleSigner(privateKeyHex); + const alg = undefined; + // Create a JWT for the request + const vcJwt: string = await didJwt.createJWT(vcPayload, { + alg: alg, + issuer: identity.did, + signer: signer, + }); + + // Make the xhr request payload + const payload = JSON.stringify({ jwtEncoded: vcJwt }); + const endorserApiServer = AppString.DEFAULT_ENDORSER_API_SERVER; + const url = endorserApiServer + "/api/v2/claim"; + const token = await accessToken(identity); + const headers = { + "Content-Type": "application/json", + Authorization: "Bearer " + token, + }; + + try { + const resp = await this.axios.post(url, payload, { headers }); + //console.log("Got resp data:", resp.data); + if (resp.data?.success) { + record.confirmed = 1; + } + } catch (error) { + let userMessage = "There was an error. See logs for more info."; + const serverError = error as AxiosError; + if (serverError) { + if (serverError.message) { + userMessage = serverError.message; // Info for the user + } else { + userMessage = JSON.stringify(serverError.toJSON()); + } + } else { + userMessage = error as string; + } + // Now set that error for the user to see. + this.alertTitle = "Error With Server"; + this.alertMessage = userMessage; + this.isAlertVisible = true; + } + } + } + + cannotConfirmMessage() { + this.alertTitle = "Not Allowed"; + this.alertMessage = "Only the recipient can confirm final receipt."; + this.isAlertVisible = true; + } + alertTitle = ""; alertMessage = ""; isAlertVisible = false; diff --git a/src/views/ContactsView.vue b/src/views/ContactsView.vue index 2e5bac98..8e693015 100644 --- a/src/views/ContactsView.vue +++ b/src/views/ContactsView.vue @@ -213,6 +213,7 @@ +