Browse Source

add ecpay

SyuanYu 1 week ago
parent
commit
e4cef2e439

+ 4 - 1
frontend/src/api.ts

@@ -1,6 +1,6 @@
 import axios from "axios";
 import { apiUrl } from "@/env";
-import type { IUserProfile, IUserProfileUpdate, IUserProfileCreate, Video, VideoCreate, ArticleCreate, ImageDownload, VideoUploaded, YTViewsUserData, PaymentData, VideoContent } from "@/interfaces";
+import type { IUserProfile, IUserProfileUpdate, IUserProfileCreate, Video, VideoCreate, ArticleCreate, ImageDownload, VideoUploaded, YTViewsUserData, PaymentData, SimplePaymentData } from "@/interfaces";
 import type { StringOptionsWithImporter } from "sass";
 
 function authHeaders(token: string) {
@@ -176,6 +176,9 @@ export const api = {
   async getPaymentList() {
     return axios.get(`${apiUrl}/api/v1/payment/list-all`);
   },
+  async simplePayment(payment_data: SimplePaymentData, lang: string) {
+    return axios.post<string>(`${apiUrl}/api/v1/payment/simpley-ecpay-payment?lang=${lang}`, payment_data);
+  },
   async uploadVideoContent(content: string, file: []) {
     // let content = "";
     console.log('api content',content);

+ 9 - 0
frontend/src/interfaces/index.ts

@@ -102,6 +102,15 @@ export interface PaymentData {
   amount: number;
 }
 
+export interface SimplePaymentData {
+  email: string,
+  name: string,
+  taxID: string,
+  tradeDesc: string,
+  item: string;
+  amount: number;
+}
+
 export interface VideoContent {
   title: string;
   text: string;

+ 19 - 1
frontend/src/stores/main.ts

@@ -4,7 +4,7 @@ import { api } from "@/api"
 import router from "@/router"
 import { getLocalToken, removeLocalToken, saveLocalToken } from "@/utils";
 import type { AppNotification } from '@/interfaces';
-import type { IUserProfile, IUserProfileCreate, IUserProfileUpdate, MainState, Video, VideoCreate, ArticleCreate, Image, ImageDownload, VideoUploaded, YTViewsUserData, PaymentData, VideoContent } from '@/interfaces';
+import type { IUserProfile, IUserProfileCreate, IUserProfileUpdate, MainState, Video, VideoCreate, ArticleCreate, Image, ImageDownload, VideoUploaded, YTViewsUserData, PaymentData, SimplePaymentData, VideoContent } from '@/interfaces';
 import i18n from '@/plugins/i18n'
 import { wsUrl } from "@/env";
 
@@ -553,6 +553,24 @@ export const useMainStore = defineStore("MainStoreId", {
         await mainStore.checkApiError(error);
       }
     },
+    async simplePayment(payment_data: SimplePaymentData, lang: string) {
+      const mainStore = useMainStore();
+      try {
+        const response = (
+          await Promise.all([
+            api.simplePayment(payment_data, lang),
+            await new Promise<void>((resolve, _) => setTimeout(() => resolve(), 0)),
+          ])
+        );
+        if (response[0]) {
+          console.log('simplePayment', response);
+
+          return response[0].data;
+        }
+      } catch (error) {
+        await mainStore.checkApiError(error);
+      }
+    },
     async uploadVideoContent(content: VideoContent, file: []) {
       console.log('uploadVideoContent');
 

+ 49 - 45
frontend/src/views/YTViews.vue

@@ -297,7 +297,9 @@ async function ECPaySubmit() {
               </v-card>
             </button>
           </v-col>
-          <p class="ms-3 error" v-show="chooseError">尚未選擇方案</p>
+          <p class="ms-3 error" v-show="chooseError">
+            <v-icon icon="error" class="me-1"></v-icon> 尚未選擇方案
+          </p>
         </v-row>
 
         <v-sheet max-width="500" class="mx-auto mt-10">
@@ -444,7 +446,7 @@ async function ECPaySubmit() {
   <div id="pay-form"></div>
 </template>
 
-<style lang="scss">
+<style lang="scss" scoped>
 .pay-card {
   .v-card {
     border: 3px solid transparent;
@@ -453,61 +455,63 @@ async function ECPaySubmit() {
     &:hover {
       border: 3px solid var(--main-color);
     }
-  }
 
-  .v-card-title {
-    h5 {
-      font-size: 20px;
-    }
-    span {
-      font-size: 16px;
-      letter-spacing: 1px;
+    &.active {
+      border: 3px solid var(--main-color);
     }
-    .price {
-      padding: 10px 0;
-      font-size: 26px;
-      font-weight: 600;
-      text-align: center;
-      color: #fff;
-      background-color: var(--main-color);
-      letter-spacing: 2px;
-      small {
-        display: block;
-        margin-top: -3px;
-        font-size: 18px;
-        font-weight: 100;
-        text-decoration: line-through;
+
+    .v-card-title {
+      h5 {
+        font-size: 20px;
+      }
+      span {
+        font-size: 16px;
+        letter-spacing: 1px;
+      }
+      .price {
+        padding: 10px 0;
+        font-size: 26px;
+        font-weight: 600;
+        text-align: center;
+        color: #fff;
+        background-color: var(--main-color);
+        letter-spacing: 2px;
+        small {
+          display: block;
+          margin-top: -3px;
+          font-size: 18px;
+          font-weight: 100;
+          text-decoration: line-through;
+        }
       }
     }
-  }
 
-  .v-card-text {
-    ul {
-      padding: 0;
-      list-style: none;
+    .v-card-text {
+      ul {
+        padding: 0;
+        list-style: none;
+      }
     }
-  }
 
-  .v-card-actions {
-    button {
-      padding: 5px 15px;
-      color: #fff;
-      border-radius: 100px;
-      background-color: var(--main-color);
-      border: 1px solid transparent;
-      &:hover {
-        color: var(--main-color);
-        background-color: #fff;
-        border: 1px solid var(--main-color);
+    .v-card-actions {
+      button {
+        padding: 5px 15px;
+        color: #fff;
+        border-radius: 100px;
+        background-color: var(--main-color);
+        border: 1px solid transparent;
+        &:hover {
+          color: var(--main-color);
+          background-color: #fff;
+          border: 1px solid var(--main-color);
+        }
       }
     }
   }
 
-  .active {
-    border: 3px solid var(--main-color);
-  }
-
   .error {
+    display: flex;
+    align-items: center;
     color: #b00020;
   }
 }

+ 139 - 171
frontend/src/views/aiReporter.vue

@@ -2,7 +2,7 @@
 import { ref, reactive, computed, watch } from "vue";
 import { required, emailRules } from "@/utils";
 import { useMainStore } from "@/stores/main";
-import type { PaymentData } from "@/interfaces";
+import type { SimplePaymentData } from "@/interfaces";
 // import type { YTViewsUserData } from "@/interfaces";
 import Navbar from "@/components/Navbar.vue";
 
@@ -32,24 +32,39 @@ let assignPrice = ref();
 function activeBtn(view: string, param: number) {
   assignView.value = view;
   assignPrice.value = param;
-  // chooseError.value = false;
-
-  console.log('assignView.value',assignView.value);
-  console.log('assignPrice.value',assignPrice.value);
-  
-  ecpay();
+  chooseError.value = false;
 }
 
+let userData = reactive({
+  email: "",
+  name: "",
+  taxID: "",
+  tradeDesc: "AI記者訂閱方案",
+});
+
+// 檢查必填欄位
+const isSubmitDisabled = computed(() => {
+  return !userData.email || !userData.name;
+});
+
 async function ecpay() {
-  const data: PaymentData = {
-    item: `AI記者訂閱制方案_${assignView.value}`,
-    amount: assignPrice.value,
-  };
-  if (data.amount <= 0) {
+  if (!assignView.value || !assignPrice.value) {
+    chooseError.value = true;
     return;
   }
 
-  const originalHTML = await mainStore.Payment(data, "zh");
+  const data: SimplePaymentData = {
+    email: userData.email,
+    name: userData.name,
+    taxID: userData.taxID,
+    tradeDesc: userData.tradeDesc,
+    item: `AI記者訂閱制_${assignView.value}`,
+    amount: assignPrice.value,
+  };
+
+  console.log("data", data);
+
+  const originalHTML = await mainStore.simplePayment(data, "zh");
 
   let formHTML = originalHTML?.replace(
     '<script type="text/javascript">document.getElementById("data_set").submit();</scr',
@@ -63,94 +78,6 @@ async function ecpay() {
   );
   ecpayForm.submit();
 }
-
-// async function ecpay() {
-//   if (!assignPrice.value) {
-//     chooseError.value = true;
-//     window.scrollTo({ top: 0, behavior: "smooth" });
-//   } else {
-//     chooseError.value = false;
-//   }
-
-//   if (checkOtherTarget.value === "其他" && otherTarget.value !== "") {
-//     userData.target = `${target.value.join("、")}、${otherTarget.value}`;
-//   } else {
-//     userData.target = target.value.join("、");
-//   }
-
-//   if (checkOtherTheme.value === "其他" && otherTheme.value !== "") {
-//     userData.theme = `${theme.value.join("、")}、${otherTheme.value}`;
-//   } else {
-//     userData.theme = theme.value.join("、");
-//   }
-
-//   let lang = ref("");
-//   let getLang = localStorage.getItem("lang");
-
-//   // 綠界顯示語言
-//   if (!getLang || getLang === "zh") {
-//     lang.value = "ZH";
-//   } else {
-//     lang.value = "ENG";
-//   }
-
-//   let data: YTViewsUserData = {
-//     item: `YT觀看數(${assignView.value} 次觀看)`,
-//     amount: assignPrice.value,
-//     email: userData.email,
-//     name: userData.name,
-//     phone: userData.phone,
-//     company: userData.company,
-//     url: userData.url,
-//     area: userData.area,
-//     language: userData.language,
-//     ages: userData.ages.join("、"),
-//     target: userData.target,
-//     theme: userData.theme,
-//     taxID: userData.taxID,
-//   };
-
-//   const originalHTML = await mainStore.YTViewsPayment(data, lang.value);
-//   let formHTML = originalHTML?.replace(
-//     '<script type="text/javascript">document.getElementById("data_set").submit();</scr',
-//     ""
-//   );
-//   formHTML = formHTML?.replace("ipt>", "");
-//   const payFormElement = document.getElementById("pay-form");
-//   payFormElement!.innerHTML = formHTML!;
-//   const ecpayForm: HTMLFormElement = <HTMLFormElement>(
-//     document.getElementById("data_set")
-//   );
-//   console.log(ecpayForm);
-//   ecpayForm.submit();
-// }
-
-// 資料存進 Google Sheets
-// async function saveData() {
-//   const scriptURL =
-//     "https://script.google.com/macros/s/AKfycbxCcfiOQ695DaxIa3peClqRRTWNj2aUNLbx7ty8U2wKlyU7wreQLioHG-sls5MPKBdlRQ/exec";
-
-//   let formdata = new FormData();
-//   formdata.append("email", userData.email);
-//   formdata.append("name", userData.name);
-//   formdata.append("company", userData.company);
-//   formdata.append("url", userData.url);
-//   formdata.append("area", userData.area);
-//   formdata.append("language", userData.language);
-//   formdata.append("age", userData.age.join("、"));
-//   formdata.append("object", userData.object);
-//   formdata.append("theme", userData.theme);
-//   formdata.append("tax", userData.tax);
-
-//   axios
-//     .post(scriptURL, formdata)
-//     .then(function (response) {
-//       console.log(response.data);
-//     })
-//     .catch(function (error) {
-//       console.log(error);
-//     });
-// }
 </script>
 
 <template>
@@ -171,7 +98,10 @@ async function ecpay() {
             :key="index"
           >
             <button @click="activeBtn(item.view, item.param)" class="w-100">
-              <v-card class="ma-3 py-3">
+              <v-card
+                class="ma-3 py-3"
+                :class="{ active: assignPrice === item.param }"
+              >
                 <v-card-title primary-title class="pa-0">
                   <div class="d-flex flex-column">
                     <h5 class="my-5 text-h5">{{ item.view }}</h5>
@@ -199,18 +129,46 @@ async function ecpay() {
                   </ul>
                 </v-card-text>
 
-                <button class="my-5 submit-btn">
-                  立即訂閱
-                </button>
-
-                <!-- <v-card-actions class="d-flex justify-center">
-                  <v-btn @click="ecpay(item.param)"> Buy Now </v-btn>
-                </v-card-actions> -->
+                <!-- <button class="my-5 submit-btn">立即訂閱</button> -->
               </v-card>
             </button>
           </v-col>
-          <p class="ms-3 error" v-show="chooseError">尚未選擇方案</p>
+          <p class="ms-3 error" v-show="chooseError">
+            <v-icon icon="error" class="me-1"></v-icon> 尚未選擇方案
+          </p>
         </v-row>
+
+        <v-sheet max-width="500" class="mx-auto mt-10">
+          <v-form @submit.prevent class="ECPay-form">
+            <v-text-field
+              v-model="userData.email"
+              :rules="emailRules()"
+              label="電子郵件"
+              required
+            ></v-text-field>
+            <v-text-field
+              v-model="userData.name"
+              :rules="required()"
+              label="姓名"
+              required
+            ></v-text-field>
+
+            <v-text-field
+              v-model="userData.taxID"
+              type="number"
+              label="是否需要統編(可填寫統編號碼)"
+            ></v-text-field>
+
+            <v-btn
+              @click="ecpay()"
+              type="submit"
+              block
+              class="mt-2 submit-btn"
+              :disabled="isSubmitDisabled"
+              >送出</v-btn
+            >
+          </v-form>
+        </v-sheet>
       </v-card-text>
     </v-card>
   </v-container>
@@ -220,84 +178,85 @@ async function ecpay() {
 
 <style lang="scss" scoped>
 .pay-card {
-  .v-card-title {
-    h5 {
-      font-weight: bold;
-    }
-    span {
-      font-size: 16px;
-      letter-spacing: 1px;
+  .v-card {
+    border: 3px solid transparent;
+    transition: all 0.3s;
+
+    &:hover {
+      border: 3px solid var(--main-color);
     }
-    .price {
-      padding: 1rem 0;
-      font-size: 26px;
-      font-weight: 600;
-      text-align: center;
-      color: #fff;
-      background-color: var(--main-color);
-      letter-spacing: 2px;
-
-      small {
-        display: block;
-        margin-top: -3px;
-        font-size: 18px;
-        font-weight: 100;
-        text-decoration: line-through;
-      }
+
+    &.active {
+      border: 3px solid var(--main-color);
     }
-  }
 
-  .v-card-text {
-    ul {
-      padding: 0;
-      list-style: none;
+    .v-card-title {
+      h5 {
+        font-weight: bold;
+      }
+      span {
+        font-size: 16px;
+        letter-spacing: 1px;
+      }
+      .price {
+        padding: 1rem 0;
+        font-size: 26px;
+        font-weight: 600;
+        text-align: center;
+        color: #fff;
+        background-color: var(--main-color);
+        letter-spacing: 2px;
+
+        small {
+          display: block;
+          margin-top: -3px;
+          font-size: 18px;
+          font-weight: 100;
+          text-decoration: line-through;
+        }
+      }
     }
-  }
 
-  .v-card-actions {
-    button {
-      padding: 5px 15px;
-      color: #fff;
-      border-radius: 100px;
-      background-color: var(--main-color);
-      border: 1px solid transparent;
-      &:hover {
-        color: var(--main-color);
-        background-color: #fff;
-        border: 1px solid var(--main-color);
+    .v-card-text {
+      ul {
+        padding: 0;
+        list-style: none;
       }
     }
   }
 
   .error {
+    display: flex;
+    align-items: center;
     color: #b00020;
   }
 
-  .v-card {
-    .submit-btn {
-      width: 150px;
-      padding: 1rem;
-      border-radius: 100px;
-      font-size: 1rem;
-      font-weight: bold;
-      color: var(--main-color);
-      border: 2px solid var(--main-color);
-      background-color: #fff;
-      letter-spacing: 1px;
-      transition: all 0.3s;
-    }
-
-    &:hover {
-      .submit-btn {
-        color: #fff;
-        background-color: var(--main-color);
-      }
-    }
-  }
+  // .v-card {
+  //   .submit-btn {
+  //     width: 150px;
+  //     padding: 1rem;
+  //     border-radius: 100px;
+  //     font-size: 1rem;
+  //     font-weight: bold;
+  //     color: var(--main-color);
+  //     border: 2px solid var(--main-color);
+  //     background-color: #fff;
+  //     letter-spacing: 1px;
+  //     transition: all 0.3s;
+  //   }
+
+  //   &:hover {
+  //     .submit-btn {
+  //       color: #fff;
+  //       background-color: var(--main-color);
+  //     }
+  //   }
+  // }
 }
 
 .ECPay-form {
   font-size: 16px;
+
   .checkbox {
     margin-left: 5px;
     list-style: none;
@@ -305,10 +264,12 @@ async function ecpay() {
       height: 40px;
     }
   }
+
   .v-input__details {
     padding-top: 0;
     padding-bottom: 3px;
   }
+
   .other {
     margin-left: 40px;
     border-bottom: 1px solid #333;
@@ -316,5 +277,12 @@ async function ecpay() {
       outline: none !important;
     }
   }
+
+  .submit-btn {
+    font-size: 1rem;
+    font-weight: bold;
+    color: #fff;
+    background-color: var(--main-color);
+  }
 }
 </style>