Procházet zdrojové kódy

入驻-添加收款账号

zhuli před 1 měsícem
rodič
revize
4472943d64
2 změnil soubory, kde provedl 452 přidání a 11 odebrání
  1. 49 0
      src/api/modules/newLoginApi.ts
  2. 403 11
      src/views/home/components/go-flow.vue

+ 49 - 0
src/api/modules/newLoginApi.ts

@@ -340,6 +340,55 @@ export const addTransferCount = (params: any) => {
 export const deleteDynamicsById = (params: any) => {
   return httpLogin.get(`/alienStore/userDynamics/deleteDynamicsById`, params);
 };
+
+//入驻   根据店铺用户ID查询支付配置(设置收款账号)
+export const getPaymentStoreUserId = (params: any) => {
+  return httpLogin.get(`/alienStore/store/payment/config/getByStoreUserId`, params);
+};
+
+/**
+ * 以 multipart/form-data 方式保存微信支付配置(公钥、私钥以文件上传,Web 端传 File)
+ * @param formFields - 除证书外的表单字段(storeUserId、apiV3Key、wechatAppId 等)
+ * @param privateKeyFile - 私钥证书文件(input type="file" 的 file)
+ * @param publicKeyFile - 公钥证书文件
+ */
+export const saveWechatWithFiles = (
+  formFields: Record<string, string>,
+  privateKeyFile?: File | null,
+  publicKeyFile?: File | null
+) => {
+  const fd = new FormData();
+  Object.entries(formFields).forEach(([key, value]) => {
+    if (value != null && value !== "") fd.append(key, String(value));
+  });
+  if (privateKeyFile) fd.append("wechatPrivateKeyFile", privateKeyFile);
+  if (publicKeyFile) fd.append("wechatPayPublicKeyFile", publicKeyFile);
+  return httpLogin.post(`/alienStore/store/payment/config/saveWechatByStoreUserId`, fd);
+};
+
+/**
+ * 以 multipart/form-data 方式保存支付宝支付配置(应用公钥、支付宝公钥、根证书以文件上传,Web 端传 File)
+ * @param formFields - 除证书外的表单字段(storeUserId、appId、appSecretCert 等)
+ * @param appPublicCertFile - 应用公钥证书文件
+ * @param alipayPublicCertFile - 支付宝公钥证书文件
+ * @param alipayRootCertFile - 支付宝根证书文件
+ */
+export const saveAlipayWithFiles = (
+  formFields: Record<string, string>,
+  appPublicCertFile?: File | null,
+  alipayPublicCertFile?: File | null,
+  alipayRootCertFile?: File | null
+) => {
+  const fd = new FormData();
+  Object.entries(formFields).forEach(([key, value]) => {
+    if (value != null && value !== "") fd.append(key, String(value));
+  });
+  if (appPublicCertFile) fd.append("appPublicCert", appPublicCertFile);
+  if (alipayPublicCertFile) fd.append("alipayPublicCert", alipayPublicCertFile);
+  if (alipayRootCertFile) fd.append("alipayRootCert", alipayRootCertFile);
+  return httpLogin.post(`/alienStore/store/payment/config/saveAlipayByStoreUserId`, fd);
+};
+
 // 根据手机号获取用户ID
 export const getUserByPhone = (params: {
   phone: string; // 手机号

+ 403 - 11
src/views/home/components/go-flow.vue

@@ -219,6 +219,11 @@
                   <el-input v-model="step2Form.storePj[2]" placeholder="请输入" maxlength="2" clearable class="store-pj-input" />
                 </div>
               </el-form-item>
+              <el-form-item label="设置收款账号" required>
+                <div style="color: #6c8ff8; cursor: pointer" @click="addPayAccount">
+                  {{ hasPayAccountConfig ? "已添加" : "添加" }}
+                </div>
+              </el-form-item>
 
               <el-form-item label="营业执照" prop="businessLicenseAddress">
                 <el-upload
@@ -264,7 +269,148 @@
       </div>
     </div>
   </div>
-
+  <!-- 设置收款账号弹窗 -->
+  <el-dialog v-model="setPayAccountDialogVisible" title="设置收款账号" width="520px" destroy-on-close append-to-body>
+    <el-form
+      ref="setPayAccountFormRef"
+      :model="setPayAccountForm"
+      :rules="setPayAccountRules"
+      label-width="140px"
+      label-position="top"
+    >
+      <el-form-item label="账号类型" prop="accountType">
+        <el-radio-group v-model="setPayAccountForm.accountType">
+          <el-radio value="wechat"> 微信 </el-radio>
+          <el-radio value="alipay"> 支付宝 </el-radio>
+        </el-radio-group>
+      </el-form-item>
+      <el-form-item label="appid" prop="wechatAppid" v-if="setPayAccountForm.accountType === 'wechat'">
+        <el-input v-model="setPayAccountForm.wechatAppid" placeholder="请输入" clearable />
+      </el-form-item>
+      <el-form-item label="mchld" prop="wechatMchld" v-if="setPayAccountForm.accountType === 'wechat'">
+        <el-input v-model="setPayAccountForm.wechatMchld" placeholder="请输入" clearable />
+      </el-form-item>
+      <el-form-item label="小程序appid" prop="wechatMiniAppId" v-if="setPayAccountForm.accountType === 'wechat'">
+        <el-input v-model="setPayAccountForm.wechatMiniAppId" placeholder="请输入" clearable />
+      </el-form-item>
+      <el-form-item label="API证书序列号" prop="wechatApiCertSerialNo" v-if="setPayAccountForm.accountType === 'wechat'">
+        <el-input v-model="setPayAccountForm.wechatApiCertSerialNo" placeholder="请输入" clearable />
+      </el-form-item>
+      <el-form-item label="APIv3 Key" prop="wechatApiV3Key" v-if="setPayAccountForm.accountType === 'wechat'">
+        <el-input v-model="setPayAccountForm.wechatApiV3Key" placeholder="请输入" clearable />
+      </el-form-item>
+      <el-form-item label="支付公钥ID" prop="wechatPayPublicKeyId" v-if="setPayAccountForm.accountType === 'wechat'">
+        <el-input v-model="setPayAccountForm.wechatPayPublicKeyId" placeholder="请输入" clearable />
+      </el-form-item>
+      <el-form-item label="私钥证书" prop="wechatPrivateKeyFile" v-if="setPayAccountForm.accountType === 'wechat'">
+        <el-upload
+          v-model:file-list="setPayAccountForm.wechatPrivateKeyFile"
+          class="set-pay-key-upload"
+          drag
+          :auto-upload="false"
+          :limit="1"
+          accept=".pem"
+          :on-exceed="() => ElMessage.warning('最多上传1个文件')"
+        >
+          <div class="set-pay-upload-box">
+            <el-icon :size="32" color="#6c8ff8">
+              <UploadFilled />
+            </el-icon>
+            <p>点击或拖拽文件至此处上传</p>
+            <span>支持.pem 格式文件,大小不超过2MB</span>
+          </div>
+        </el-upload>
+      </el-form-item>
+      <el-form-item label="公钥证书" prop="wechatPayPublicKeyFile" v-if="setPayAccountForm.accountType === 'wechat'">
+        <el-upload
+          v-model:file-list="setPayAccountForm.wechatPayPublicKeyFile"
+          class="set-pay-key-upload"
+          drag
+          :auto-upload="false"
+          :limit="1"
+          accept=".pem"
+          :on-exceed="() => ElMessage.warning('最多上传1个文件')"
+        >
+          <div class="set-pay-upload-box">
+            <el-icon :size="32" color="#6c8ff8">
+              <UploadFilled />
+            </el-icon>
+            <p>点击或拖拽文件至此处上传</p>
+            <span>支持.pem 格式文件,大小不超过2MB</span>
+          </div>
+        </el-upload>
+      </el-form-item>
+
+      <!--支付宝--->
+      <el-form-item label="应用ID" prop="aliAppid" v-if="setPayAccountForm.accountType === 'alipay'">
+        <el-input v-model="setPayAccountForm.aliAppid" placeholder="请输入" clearable />
+      </el-form-item>
+      <el-form-item label="应用私钥" prop="aliPrivateId" v-if="setPayAccountForm.accountType === 'alipay'">
+        <el-input v-model="setPayAccountForm.aliPrivateId" placeholder="请输入" clearable />
+      </el-form-item>
+      <el-form-item label="应用公钥证书" prop="appPublicCertFile" v-if="setPayAccountForm.accountType === 'alipay'">
+        <el-upload
+          v-model:file-list="setPayAccountForm.appPublicCertFile"
+          class="set-pay-key-upload"
+          drag
+          :auto-upload="false"
+          :limit="1"
+          accept=".pem"
+          :on-exceed="() => ElMessage.warning('最多上传1个文件')"
+        >
+          <div class="set-pay-upload-box">
+            <el-icon :size="32" color="#6c8ff8">
+              <UploadFilled />
+            </el-icon>
+            <p>点击或拖拽文件至此处上传</p>
+            <span>支持.pem 格式文件,大小不超过2MB</span>
+          </div>
+        </el-upload>
+      </el-form-item>
+      <el-form-item label="支付宝公钥证书" prop="alipayPublicCertFile" v-if="setPayAccountForm.accountType === 'alipay'">
+        <el-upload
+          v-model:file-list="setPayAccountForm.alipayPublicCertFile"
+          class="set-pay-key-upload"
+          drag
+          :auto-upload="false"
+          :limit="1"
+          accept=".pem"
+          :on-exceed="() => ElMessage.warning('最多上传1个文件')"
+        >
+          <div class="set-pay-upload-box">
+            <el-icon :size="32" color="#6c8ff8">
+              <UploadFilled />
+            </el-icon>
+            <p>点击或拖拽文件至此处上传</p>
+            <span>支持.pem 格式文件,大小不超过2MB</span>
+          </div>
+        </el-upload>
+      </el-form-item>
+      <el-form-item label="支付宝根证书" prop="alipayRootCertFile" v-if="setPayAccountForm.accountType === 'alipay'">
+        <el-upload
+          v-model:file-list="setPayAccountForm.alipayRootCertFile"
+          class="set-pay-key-upload"
+          drag
+          :auto-upload="false"
+          :limit="1"
+          accept=".pem"
+          :on-exceed="() => ElMessage.warning('最多上传1个文件')"
+        >
+          <div class="set-pay-upload-box">
+            <el-icon :size="32" color="#6c8ff8">
+              <UploadFilled />
+            </el-icon>
+            <p>点击或拖拽文件至此处上传</p>
+            <span>支持.pem 格式文件,大小不超过2MB</span>
+          </div>
+        </el-upload>
+      </el-form-item>
+    </el-form>
+    <template #footer>
+      <el-button @click="setPayAccountDialogVisible = false"> 取消 </el-button>
+      <el-button type="primary" @click="submitSetPayAccount"> 确定 </el-button>
+    </template>
+  </el-dialog>
   <!-- 图片预览 -->
   <el-image-viewer
     v-if="imageViewerVisible"
@@ -275,19 +421,21 @@
 </template>
 
 <script setup lang="ts">
-import { ref, reactive, watch, onMounted, computed } from "vue";
+import { ref, reactive, watch, onMounted, computed, toRaw } from "vue";
 import { ElMessage, ElMessageBox, type FormInstance, type FormRules, UploadUserFile, UploadRequestOptions } from "element-plus";
 import { Plus } from "@element-plus/icons-vue";
 
+import { applyStore, getMerchantByPhone, getThirdLevelList, verifyIdInfo } from "@/api/modules/homeEntry";
 import {
-  applyStore,
-  getMerchantByPhone,
-  getFirstLevelList,
-  getSecondLevelList,
-  getThirdLevelList,
-  verifyIdInfo
-} from "@/api/modules/homeEntry";
-import { getInputPrompt, getDistrict, uploadImg, ocrRequestUrl, getAiapprovestoreInfo } from "@/api/modules/newLoginApi";
+  getInputPrompt,
+  getDistrict,
+  uploadImg,
+  ocrRequestUrl,
+  getAiapprovestoreInfo,
+  getPaymentStoreUserId,
+  saveWechatWithFiles,
+  saveAlipayWithFiles
+} from "@/api/modules/newLoginApi";
 import { localGet, localSet } from "@/utils/index";
 import { useAuthStore } from "@/stores/modules/auth";
 
@@ -299,7 +447,136 @@ const showDisportLicence = ref(false);
 const imageViewerVisible = ref(false);
 const imageViewerUrlList = ref<string[]>([]);
 const imageViewerInitialIndex = ref(0);
+//设置收款账号
+const setPayAccountDialogVisible = ref(false);
+const hasPayAccountConfig = ref(false);
+const setPayAccountForm = reactive({
+  accountType: "wechat",
+  wechatAppid: "",
+  wechatMchld: "",
+  wechatMiniAppId: "",
+  wechatApiCertSerialNo: "",
+  wechatApiV3Key: "",
+  wechatPayPublicKeyId: null,
+  wechatPrivateKeyFile: [] as UploadUserFile[],
+  wechatPayPublicKeyFile: [] as UploadUserFile[],
+
+  aliAppid: "",
+  aliPrivateId: "",
+  alipayRootCertFile: [] as UploadUserFile[],
+  alipayPublicCertFile: [] as UploadUserFile[],
+  appPublicCertFile: [] as UploadUserFile[]
+});
+const setPayAccountRules = ref({
+  accountType: [{ required: true, message: "请选择账号类型", trigger: "change" }],
+  wechatAppid: [{ required: true, message: "请输入appid", trigger: "blur" }],
+  wechatMchld: [{ required: true, message: "请输入mchId", trigger: "blur" }],
+  wechatApiCertSerialNo: [{ required: true, message: "请输入API证书序列号", trigger: "blur" }],
+  wechatApiV3Key: [{ required: true, message: "请输入APIv3 Key", trigger: "blur" }],
+  wechatPayPublicKeyId: [{ required: true, message: "请输入支付公钥ID", trigger: "blur" }],
+  wechatMiniAppId: [{ required: true, message: "请输入小程序AppId", trigger: "blur" }],
+  wechatPrivateKeyFile: [{ required: true, type: "array", min: 1, message: "请上传私钥证书", trigger: "change" }],
+  wechatPayPublicKeyFile: [{ required: true, type: "array", min: 1, message: "请上传公钥证书", trigger: "change" }]
+});
+const setPayAccountFormRef = ref<FormInstance>();
+const submitSetPayAccount = async () => {
+  if (!setPayAccountFormRef.value) return;
+  await setPayAccountFormRef.value.validate(async valid => {
+    if (!valid) return;
+
+    const storeUserId = localGet("geeker-user")?.userInfo?.id;
+    if (!storeUserId) {
+      ElMessage.warning("请先登录");
+      return;
+    }
+    if (setPayAccountForm.accountType === "alipay") {
+      const appPublicCertList = setPayAccountForm.appPublicCertFile as UploadUserFile[];
+      const alipayPublicCertList = setPayAccountForm.alipayPublicCertFile as UploadUserFile[];
+      const alipayRootCertList = setPayAccountForm.alipayRootCertFile as UploadUserFile[];
+
+      const appPublicCertFile = appPublicCertList[0]?.raw as File | undefined;
+      const alipayPublicCertFile = alipayPublicCertList[0]?.raw as File | undefined;
+      const alipayRootCertFile = alipayRootCertList[0]?.raw as File | undefined;
+
+      if (!setPayAccountForm.aliAppid?.trim()) {
+        ElMessage.warning("请输入应用ID");
+        return;
+      }
+      if (!setPayAccountForm.aliPrivateId?.trim()) {
+        ElMessage.warning("请输入应用私钥");
+        return;
+      }
+      if (!appPublicCertFile || !alipayPublicCertFile || !alipayRootCertFile) {
+        ElMessage.warning("请上传应用公钥证书、支付宝公钥证书和支付宝根证书");
+        return;
+      }
+
+      try {
+        const res: any = await saveAlipayWithFiles(
+          {
+            storeUserId: String(storeUserId),
+            appId: setPayAccountForm.aliAppid.trim(),
+            appSecretCert: setPayAccountForm.aliPrivateId.trim()
+          },
+          appPublicCertFile,
+          alipayPublicCertFile,
+          alipayRootCertFile
+        );
+        if (res?.code === 200) {
+          ElMessage.success("保存成功");
+          hasPayAccountConfig.value = true;
+          setPayAccountDialogVisible.value = false;
+        } else {
+          ElMessage.error(res?.msg || "保存失败");
+        }
+      } catch (e: any) {
+        ElMessage.error(e?.message || "保存失败");
+      }
+      return;
+    }
+    if (setPayAccountForm.accountType === "wechat") {
+      const privateKeyUpload = (setPayAccountForm.wechatPrivateKeyFile as UploadUserFile[])[0];
+      const publicKeyUpload = (setPayAccountForm.wechatPayPublicKeyFile as UploadUserFile[])[0];
+
+      const privateKeyFile = privateKeyUpload?.raw as File | undefined;
+      const publicKeyFile = publicKeyUpload?.raw as File | undefined;
+
+      if (!privateKeyFile || !publicKeyFile) {
+        ElMessage.warning("请上传私钥证书和公钥证书");
+        return;
+      }
 
+      try {
+        const res: any = await saveWechatWithFiles(
+          {
+            storeUserId: String(storeUserId),
+            apiV3Key: setPayAccountForm.wechatApiV3Key || "",
+            merchantSerialNumber: setPayAccountForm.wechatApiCertSerialNo || "",
+            wechatAppId: setPayAccountForm.wechatAppid || "",
+            wechatMchId: setPayAccountForm.wechatMchld || "",
+            wechatMiniAppId: setPayAccountForm.wechatMiniAppId || "",
+            wechatPayPublicKeyId: setPayAccountForm.wechatPayPublicKeyId || "",
+            accountType: setPayAccountForm.accountType || "wechat"
+          },
+          privateKeyFile,
+          publicKeyFile
+        );
+        if (res?.code === 200) {
+          ElMessage.success("保存成功");
+          hasPayAccountConfig.value = true;
+          setPayAccountDialogVisible.value = false;
+        } else {
+          ElMessage.error(res?.msg || "保存失败");
+        }
+      } catch (e: any) {
+        ElMessage.error(e?.message || "保存失败");
+      }
+    }
+
+    if (setPayAccountForm.accountType === "alipay") {
+    }
+  });
+};
 const entryList = ref([
   {
     title: "个人实名"
@@ -597,6 +874,81 @@ watch(
   }
 );
 
+/** 将后端返回的 base64 证书转为 File,用于回显且提交时可原样上传 */
+const base64ToFile = (base64Str: string, fileName: string): File => {
+  const bstr = atob(base64Str);
+  const u8arr = new Uint8Array(bstr.length);
+  for (let i = 0; i < bstr.length; i++) u8arr[i] = bstr.charCodeAt(i);
+  return new File([u8arr], fileName, { type: "application/x-pem-file" });
+};
+
+const fetchPayAccountConfig = async () => {
+  const storeUserId = localGet("geeker-user")?.userInfo?.id;
+  if (!storeUserId) return;
+  const res: any = await getPaymentStoreUserId({ storeUserId });
+  if (res?.code !== 200 || !res.data) {
+    hasPayAccountConfig.value = false;
+    return;
+  }
+  const data = res.data;
+  hasPayAccountConfig.value = !!(data.wechatAppId || data.appId);
+
+  // 根据已有数据决定默认展示类型:有微信配置优先微信,否则支付宝
+  if (data.wechatAppId) {
+    setPayAccountForm.accountType = "wechat";
+    setPayAccountForm.wechatAppid = data.wechatAppId ?? "";
+    setPayAccountForm.wechatMchld = data.wechatMchId ?? "";
+    setPayAccountForm.wechatMiniAppId = data.wechatMiniAppId ?? "";
+    setPayAccountForm.wechatApiCertSerialNo = data.merchantSerialNumber ?? "";
+    setPayAccountForm.wechatApiV3Key = data.apiV3Key ?? "";
+    setPayAccountForm.wechatPayPublicKeyId = data.wechatPayPublicKeyId ?? null;
+    setPayAccountForm.wechatPrivateKeyFile = [];
+    setPayAccountForm.wechatPayPublicKeyFile = [];
+    if (data.wechatPrivateKeyFile && data.wechatPrivateKeyName) {
+      const file = base64ToFile(data.wechatPrivateKeyFile, data.wechatPrivateKeyName);
+      setPayAccountForm.wechatPrivateKeyFile = [
+        { name: file.name, raw: file, status: "success", uid: Date.now() } as UploadUserFile
+      ];
+    }
+    if (data.wechatPayPublicKeyFile && data.wechatPayPublicKeyFileName) {
+      const file = base64ToFile(data.wechatPayPublicKeyFile, data.wechatPayPublicKeyFileName);
+      setPayAccountForm.wechatPayPublicKeyFile = [
+        { name: file.name, raw: file, status: "success", uid: Date.now() + 1 } as UploadUserFile
+      ];
+    }
+  }
+
+  if (data.appId) {
+    setPayAccountForm.aliAppid = data.appId ?? "";
+    setPayAccountForm.aliPrivateId = data.appSecretCert ?? "";
+    setPayAccountForm.appPublicCertFile = [];
+    setPayAccountForm.alipayPublicCertFile = [];
+    setPayAccountForm.alipayRootCertFile = [];
+    if (data.appPublicCert && data.appPublicCertName) {
+      const file = base64ToFile(data.appPublicCert, data.appPublicCertName);
+      setPayAccountForm.appPublicCertFile = [
+        { name: file.name, raw: file, status: "success", uid: Date.now() } as UploadUserFile
+      ];
+    }
+    if (data.alipayPublicCert && data.alipayPublicCertName) {
+      const file = base64ToFile(data.alipayPublicCert, data.alipayPublicCertName);
+      setPayAccountForm.alipayPublicCertFile = [
+        { name: file.name, raw: file, status: "success", uid: Date.now() + 1 } as UploadUserFile
+      ];
+    }
+    if (data.alipayRootCert && data.alipayRootCertName) {
+      const file = base64ToFile(data.alipayRootCert, data.alipayRootCertName);
+      setPayAccountForm.alipayRootCertFile = [
+        { name: file.name, raw: file, status: "success", uid: Date.now() + 2 } as UploadUserFile
+      ];
+    }
+    if (!data.wechatAppId) setPayAccountForm.accountType = "alipay";
+  }
+};
+const addPayAccount = () => {
+  setPayAccountDialogVisible.value = true;
+  fetchPayAccountConfig();
+};
 onMounted(() => {
   callGetUserInfo();
   if (currentStep.value === 3 && (storeApplicationStatus.value === 0 || storeApplicationStatus.value === 2)) {
@@ -1301,7 +1653,12 @@ const handleExceed = () => {
   ElMessage.warning("文件数量超出限制");
 };
 </script>
-
+<style>
+.el-dialog__body {
+  height: 600px;
+  overflow: scroll;
+}
+</style>
 <style scoped lang="scss">
 // 店铺评价三个输入框纵向排列
 .store-pj-inputs {
@@ -1507,4 +1864,39 @@ const handleExceed = () => {
     }
   }
 }
+.set-pay-upload-box {
+  display: flex;
+  flex-direction: column;
+  gap: 8px;
+  align-items: center;
+  justify-content: center;
+  padding: 24px;
+  cursor: pointer;
+  background: #fafafa;
+  border: 1px dashed #dcdfe6;
+  border-radius: 8px;
+  transition: border-color 0.2s;
+  &:hover {
+    border-color: #6c8ff8;
+  }
+  p {
+    margin: 0;
+    font-size: 14px;
+    color: #606266;
+  }
+  span {
+    font-size: 12px;
+    color: #909399;
+  }
+}
+.set-pay-key-upload {
+  width: 100%;
+  :deep(.el-upload) {
+    width: 100%;
+  }
+  :deep(.el-upload-dragger) {
+    width: 100%;
+    padding: 32px 16px;
+  }
+}
 </style>