Skip to content

Commit

Permalink
5921 payment flow modification (#1113)
Browse files Browse the repository at this point in the history
* chore(marketplace): Bump packages

* feat(marketplace): A modal with cert holder

* chore(marketplace): Remove comment

* fix(marketplace): Make important text bold and remove unused component

* fix(marketplace): Add @rollup/rollup-linux-x64-gnu as optional dep

* feat(marketplace): Add a second call to retire credits after they are bought

* fix(marketplace): Sloppy previous commit

* fix(marketplace): Retire credits immediately properly
  • Loading branch information
jschill authored Dec 20, 2023
1 parent 481f33b commit 3545e16
Show file tree
Hide file tree
Showing 8 changed files with 356 additions and 462 deletions.
572 changes: 126 additions & 446 deletions frontend/marketplace/package-lock.json

Large diffs are not rendered by default.

15 changes: 9 additions & 6 deletions frontend/marketplace/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,18 @@
"@apollo/client": "^3.8.7",
"@empower-plastic/empowerjs": "0.0.18",
"@keplr-wallet/types": "^0.12.44",
"@logto/vue": "^2.1.0",
"@logto/vue": "^2.1.1",
"@vue/apollo-composable": "^4.0.0-beta.5",
"@vue/apollo-option": "^4.0.0-beta.9",
"@vuepic/vue-datepicker": "^7.2.2",
"daisyui": "^4.4.4",
"daisyui": "^4.4.20",
"graphql": "^16.8.1",
"graphql-tag": "^2.12.6",
"jspdf": "^2.5.1",
"jspdf-autotable": "^3.7.1",
"qrcode": "^1.5.3",
"rollbar": "^2.26.2",
"vue": "^3.3.2",
"vue": "^3.3.12",
"vue-awesome-paginate": "^1.1.46",
"vue-multiselect": "^3.0.0-beta.2",
"vue-router": "^4.2.5",
Expand Down Expand Up @@ -57,13 +57,16 @@
"postcss": "^8.4.31",
"prettier": "^3.1.0",
"tailwindcss": "^3.3.5",
"typescript": "~5.3.2",
"vite": "^5.0.5",
"typescript": "~5.3.3",
"vite": "^5.0.10",
"vitest": "^0.34.6",
"vue-tsc": "^1.8.22"
"vue-tsc": "^1.8.25"
},
"engines": {
"npm": ">=10.2.3",
"node": ">=20.10.0"
},
"optionalDependencies": {
"@rollup/rollup-linux-x64-gnu": "^4.9.1"
}
}
22 changes: 16 additions & 6 deletions frontend/marketplace/src/components/BuyButton.vue
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
<script setup lang="ts">
import { computed } from "vue";
import { computed, ref } from "vue";
import { useAuth } from "@/stores/auth";
import { isValidCreditAmount } from "@/utils/utils";
import CertificateHolderModal from "@/components/CertificateHolderModal.vue";
export interface BuyButtonProps {
showButtonSpinner: boolean;
insufficientBalance: boolean;
coinFormatted: string;
handleCardPayment: () => void;
handleBuyCredits: () => void;
handleCardPayment: (name: string) => void;
handleBuyCredits: (name: string) => void;
isWalletConnected: boolean;
availableCredits: number;
buyingCreditAmount: number;
}
const props = defineProps<BuyButtonProps>();
const { isAuthenticated, handleSignIn } = useAuth();
const modalEl = ref<HTMLDialogElement | null>(null);
const continueHandler = ref<((name: string) => void) | undefined>(undefined);
enum BuyButtonState {
DISABLED = "disabled",
Expand Down Expand Up @@ -68,12 +70,19 @@ const buttonText = computed(() => {
}
});
const addModalToHandler = (newContinueHandler: (name: string) => void) => {
return () => {
continueHandler.value = newContinueHandler;
modalEl.value?.show();
};
};
const buttonHandler = computed<(() => void) | undefined>(() => {
switch (buttonState.value) {
case BuyButtonState.ENABLED_CARD:
return props.handleCardPayment;
return addModalToHandler(props.handleCardPayment);
case BuyButtonState.ENABLED_WALLET:
return props.handleBuyCredits;
return addModalToHandler(props.handleBuyCredits);
case BuyButtonState.ENABLED_UNAUTHORIZED:
return handleSignIn;
default:
Expand Down Expand Up @@ -114,4 +123,5 @@ const buttonsCssClasses = `
<span v-if="showButtonSpinner" class="loading loading-spinner"></span>
{{ buttonText }}
</button>
<CertificateHolderModal ref="modalEl" @continue="continueHandler" />
</template>
29 changes: 25 additions & 4 deletions frontend/marketplace/src/components/BuyCredits.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { useFetcher, authHeader } from "@/utils/fetcher";
import { useAuth } from "@/stores/auth";
import { useWallet } from "@/stores/wallet";
import { useNotifyer } from "@/utils/notifyer";
import { useRetireCredits } from "@/utils/plastic-credits";
export interface BuyCreditsProps {
availableCredits: number;
Expand All @@ -29,7 +30,7 @@ export interface BuyCreditsProps {
}
const { isAuthenticated, getAccessToken } = useAuth();
const { isWalletConnected } = useWallet();
const { isWalletConnected, address: walletAddress } = useWallet();
const { notifyer } = useNotifyer();
const amount = ref<number>(1);
const props = defineProps<BuyCreditsProps>();
Expand All @@ -40,6 +41,7 @@ const currentBalance = ref(Number.MAX_SAFE_INTEGER);
const availableCreditsString = computed<string>(() => {
return `${props.availableCredits}/${props.initialCredits}`;
});
const retireCredits = useRetireCredits();
watch(isWalletConnected, async (newVal) => {
if (newVal === true) {
Expand Down Expand Up @@ -89,8 +91,8 @@ const checkBalanceForPurchase = (amount: number) => {
}
};
const handleBuyCredits = async () => {
if (!isWalletConnected.value) {
const handleBuyCredits = async (retirererName: string) => {
if (!isWalletConnected.value || !walletAddress.value) {
notifyer.error("Please connect to wallet");
return;
}
Expand Down Expand Up @@ -143,6 +145,24 @@ const handleBuyCredits = async () => {
} finally {
showButtonSpinner.value = false;
}
try {
await retireCredits.handleRetireCredits(
amount.value,
walletAddress.value,
props.denom,
retirererName,
() => {
notifyer.success(
"Retired credits successfully and generated a certificate",
);
},
);
notifyer.success("Retire credits successfully");
} catch (error) {
notifyer.error("Retire credits failed: " + resolveSdkError(error));
throw new Error("Error retiring credits, " + error);
}
};
const checkIfCreditsAvailable = () => {
Expand All @@ -153,7 +173,7 @@ const checkIfCreditsAvailable = () => {
return true;
};
const handleCardPayment = async () => {
const handleCardPayment = async (retirererName: string) => {
if (!checkIfCreditsAvailable()) {
return;
}
Expand All @@ -171,6 +191,7 @@ const handleCardPayment = async () => {
amount: amount.value,
denom: props.denom,
listingOwner: props.owner,
retirererName: retirererName,
};
try {
const accessToken = await getAccessToken(PC_BACKEND_ENDPOINT);
Expand Down
60 changes: 60 additions & 0 deletions frontend/marketplace/src/components/CertificateHolderModal.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<script setup lang="ts">
import { ref, computed } from "vue";
import Modal from "@/components/ui/Modal.vue";
import PrimaryButton from "@/components/ui/PrimaryButton.vue";
const emit = defineEmits(["continue"]);
const baseModal = ref<HTMLDialogElement | null>(null);
const certificateHolder = ref<string>("");
const isCertificateHolderValid = computed<boolean>(() => {
return (
certificateHolder.value.length > 0 && certificateHolder.value.length < 31
);
});
const isCertificateHolderTooLong = computed<boolean>(() => {
return certificateHolder.value.length > 30;
});
const handleOpenModal = () => {
baseModal.value?.show();
};
const handleContinue = () => {
emit("continue", certificateHolder.value);
baseModal.value?.close();
};
defineExpose({
show: handleOpenModal,
});
</script>
<template>
<Modal ref="baseModal" title="Certificate holder">
<template v-slot:body>
<p class="py-4">
Provide an individual or company name that is going to be used for the
Plastic Credit Offset Certificate.
<b>Mind that this cannot be changed later.</b>
</p>
<div>
<input
type="text"
placeholder="Certificate holder name"
class="input input-bordered w-full text-black"
v-model="certificateHolder"
/>
<div class="label" v-if="isCertificateHolderTooLong">
<span class="label-text-alt text-white"
>Name can not be more than 30 characters.</span
>
</div>
</div>
</template>
<template v-slot:actions>
<PrimaryButton
@click.prevent="handleContinue"
:disabled="!isCertificateHolderValid"
>Continue</PrimaryButton
>
</template>
</Modal>
</template>
41 changes: 41 additions & 0 deletions frontend/marketplace/src/components/ui/Modal.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<script setup lang="ts">
import { ref, defineExpose } from "vue";
interface Props {
title?: string;
}
const props = defineProps<Props>();
const isOpen = ref(false);
const dialog = ref<HTMLDialogElement>();
const show = () => {
isOpen.value = true;
dialog.value?.showModal();
};
const close = () => {
isOpen.value = false;
dialog.value?.close();
};
defineExpose({
show,
close,
});
</script>

<template>
<dialog ref="dialog" id="modal_dialog" class="modal">
<div class="modal-box bg-modalBackground">
<form method="dialog">
<button class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2">
</button>
</form>
<h3 v-if="props.title" class="font-bold text-lg">{{ props.title }}</h3>
<slot name="body" />
<div class="modal-action">
<slot name="actions"></slot>
</div>
</div>
</dialog>
</template>
16 changes: 16 additions & 0 deletions frontend/marketplace/src/components/ui/PrimaryButton.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<script setup lang="ts">
import type { ButtonHTMLAttributes, ReservedProps } from "vue";
interface Props
extends /* @vue-ignore */ ButtonHTMLAttributes,
/* @vue-ignore */ ReservedProps {}
const props = defineProps<Props>();
</script>
<template>
<button
v-bind="props"
class="btn btn-ghost text-white btn-block normal-case bg-greenPrimary hover:bg-greenDark text-title24 lg:text-title32 lg:btn-lg p-0 px-12 font-normal disabled:bg-lightGray disabled:text-white"
>
<slot />
</button>
</template>
63 changes: 63 additions & 0 deletions frontend/marketplace/src/utils/plastic-credits.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { ref } from "vue";
import {
empowerchain,
getSigningTM37EmpowerchainClient,
} from "@empower-plastic/empowerjs";
import { getWallet } from "@/utils/wallet-utils";
import { CHAIN_ID, RPC_ENDPOINT } from "@/config/config";

const { retireCredits } =
empowerchain.plasticcredit.MessageComposer.withTypeUrl;

export const useRetireCredits = () => {
const loading = ref(false);

const handleRetireCredits = async (
amount: any, // Typing, Johannes?!
address: string,
denom: string,
retirererName: string,
onSuccess: () => void,
additionalInfo?: string,
) => {
try {
loading.value = true;
const retireCreditsMsg = retireCredits({
owner: address,
denom: denom,
amount: amount as bigint,
retiringEntityName: retirererName,
retiringEntityAdditionalData: additionalInfo ?? "",
});
const wallet = getWallet();
const offlineSigner = wallet.getOfflineSigner(CHAIN_ID);
const chainClient = await getSigningTM37EmpowerchainClient({
rpcEndpoint: RPC_ENDPOINT,
signer: offlineSigner,
});
const fee = {
amount: [{ amount: "100000", denom: "umpwr" }],
gas: "200000",
};
const response = await chainClient.signAndBroadcast(
address,
[retireCreditsMsg],
fee,
);
if (response && !response.code) {
loading.value = false;
onSuccess();
} else {
throw new Error("Retire credits failed " + response.rawLog);
}
} catch (error) {
loading.value = false;
throw error;
}
};

return {
handleRetireCredits,
loading,
};
};

0 comments on commit 3545e16

Please sign in to comment.