Переглянути джерело

feat: "TODO: 空中云汇付款提现详情"

wangzeyu@tom.com 2 роки тому
батько
коміт
7a0dea4bb2

+ 5 - 1
src/assets/language/en.json

@@ -597,10 +597,14 @@
     }
   },
   "airwallex": {
+    "sunzee": "Sunzee",
     "airwallex": "Airwallex",
     "wallet": "Airwallex wallet",
     "amount": "Airwallex amount",
-    "payment": "New transfer"
+    "payment": "New transfer",
+    "paymentDetail": "Payment details",
+    "payout": "Payout",
+    "beneficiary": "Beneficiary"
   },
   "joinpayMch": {
     "withdrawalAccountNo": "Withdrawal account No",

+ 5 - 1
src/assets/language/zh.json

@@ -598,10 +598,14 @@
     }
   },
   "airwallex": {
+    "sunzee": "申泽智能",
     "airwallex": "空中云汇",
     "wallet": "空中云汇钱包",
     "amount": "钱包金额",
-    "payment": "付款转账"
+    "payment": "付款转账",
+    "paymentDetail": "付款详情",
+    "payout": "付款单",
+    "beneficiary": "收款人"
   },
   "joinpayMch": {
     "withdrawalAccountNo": "提现账号",

+ 1 - 0
src/constants/const.js

@@ -0,0 +1 @@
+export const AIRWALLEX_ADMIN_CLIENTID = 'IBi-pVO4SSGzXIDHRYfVOA';

+ 25 - 4
src/router/index.js

@@ -202,11 +202,32 @@ const router = createRouter({
       component: () => import("@/views/airwallex/index.vue"),
       meta: { index: 1 },
     },
-    // Airwallex 提现
+    // Airwallex 创建付款人
     {
-      path: "/airwallexPayment",
-      name: "airwallexPayment",
-      component: () => import("@/views/airwallex/payment.vue"),
+      path: "/airwallexBeneficiary",
+      name: "airwallexBeneficiary",
+      component: () => import("@/views/airwallex/beneficiary.vue"),
+      meta: { index: 1 },
+    },
+    // Airwallex 付款单
+    {
+      path: "/airwallexPayout",
+      name: "airwallexPayout",
+      component: () => import("@/views/airwallex/payout.vue"),
+      meta: { index: 1},
+    },
+    // Airwallex 去付款
+    {
+      path: "/caPayment",
+      name: "caPayment",
+      component: () => import("@/views/airwallex/caPayment.vue"),
+      meta: { index: 1 },
+    },
+    // Airwallex 付款结果详情
+    {
+      path: "/paymentDetail",
+      name: "paymentDetail",
+      component: () => import("@/views/airwallex/paymentDetail.vue"),
       meta: { index: 1 },
     },
     {

+ 18 - 2
src/service/airwallex/index.js

@@ -2,5 +2,21 @@ import axios from '../../utils/axios';
 
 // 获取 AuthCode
 export function getAuthCode(getAuthCodeDTO) {
-    return axios.post(`/SZWL-SERVER/api/airwallexPay/AuthAnAccount`, getAuthCodeDTO);
-}
+    return axios.post(`/PAY-SERVER/api/airwallexPay/getAuthCode`, getAuthCodeDTO);
+}
+
+// 创建收款人
+export function createBeneficiary(payloadData) {
+    return axios.post(`/PAY-SERVER/api/airwallexPay/caBeneficiary`, payloadData);
+}
+
+// 新建一笔付款
+export function createPayment(reqData) {
+    return axios.post(`/PAY-SERVER/api/airwallexPay/caPayment`, reqData);
+}
+
+// 查询付款详情
+export function getPaymentById(paymentId) {
+    return axios.get(`/PAY-SERVER/api/airwallex/getPaymentById/${paymentId}`);
+}
+

+ 70 - 14
src/styles/airwallex/index.less

@@ -1,22 +1,78 @@
 .airwallexWallet {
   background-color: #f8f8f8;
+  .amount {
+    margin-top: 40px;
+  }
+  .airwallexWalletBtn {
+    width: 100%;
+    margin-top: 20px;
+    text-align: center;
+    .register {
+      background-color: #4d6add;
+      border-radius: 17px;
+      height: 34px;
+      width: 220px;
+      margin-top: 60px;
+      font-size: 15px;
+      font-family: PingFangSC-Medium;
+    }
+  }
+  .cell-container {
+    margin-top: 20px;
+    /* 自定义间距,根据需要调整 */
+  }
 }
 
-.airwallexWallet .amount {
-  margin-top: 40px;
+.airwallexPayment {
+  width: 100%;
+  max-width: 100%; /* 设置上面的 div 的最大高度 */
+  background-color: #f8f8f8;
+  min-height: 100vh;
+  overflow: auto;
+  display: flex;
+  flex-direction: column;
+  position: relative;
+  overflow-x: hidden; /* 禁用底部水平滚动条 */
+  .form-container {
+    width: auto;
+    height: 620px;
+    background-repeat: no-repeat;
+    position: relative; /* 设置父容器为相对定位 */
+    // background-color: rgb(252, 240, 230);
+  }
+  // .form-container::after {
+  //   content: "";
+  //   position: absolute;
+  //   bottom: -1px; /* 将伪元素定位在下边框位置 */
+  //   left: 0;
+  //   width: 100%;
+  //   height: 3px; /* 渐变线高度 */
+  //   background-image: linear-gradient(to right, #f00, #0f0); /* 渐变色 */
+  // }
+  .button-container {
+    display: flex;
+    justify-content: space-between;
+    position: fixed; /* 让按钮固定在底部 */
+    bottom: 50px; /* 距离底部的距离 */
+    width: 100%;
+    align-items: center;
+  }
+  .cancel {
+    margin-left: 15%;
+  }
+  .register {
+    margin-right: 15%;
+  }
 }
 
-.airwallexWallet .airwallexWalletBtn{
-  width: 100%;
-  margin-top: 20px;
-  text-align: center;
+.payout-form-container {
+  overflow-y: auto; /* 配置右侧垂直滚动条 */
+  padding: 10px;
+  flex-grow: 1;
 }
-.airwallexWallet .airwallexWalletBtn .register {
-  background-color: #4d6add;
-  border-radius: 17px;
-  height: 34px;
-  width: 220px;
-  margin-top: 60px;
-  font-size: 15px;
-  font-family: PingFangSC-Medium;
+
+.beneficiary-form-container {
+  overflow-y: auto; /* 配置右侧垂直滚动条 */
+  padding: 10px;
+  flex-grow: 1;
 }

+ 7 - 3
src/utils/config.js

@@ -2,19 +2,23 @@ let baseURL;
 // 开发环境
 if (process.env.NODE_ENV === 'development') {
     // 测试的
-    // baseURL = 'http://szwltest.sunzee.com.cn:49002/'
-    baseURL = 'https://szwl.sunzee.com.cn/'
+    baseURL = 'http://szwltest.sunzee.com.cn:49002/'
+    // baseURL = 'https://szwl.sunzee.com.cn/'
     // 正式的
     // baseURL = 'https://sz.sunzee.com.cn/'
     
     // baseURL = 'http://120.25.151.99:49002/'
     // 编译环境
     // baseURL = 'http://127.0.0.1:8080/'
+    // baseURL = 'https://xavi.c1.sidoc.cn'
 } else {
+    // 判断当前页面的主机名是否包含 'szwltest.sunzee.com.cn'。如果包含,则条件成立(即为 true);否则,条件不成立
     if (window.location.host.indexOf('szwltest.sunzee.com.cn') !== -1) {
-        baseURL = 'https://szwl.sunzee.com.cn/'
+        // baseURL = 'https://szwl.sunzee.com.cn/'
+        baseURL = 'https://szwltest.sunzee.com.cn/'
     } else {
         baseURL = 'https://sz.sunzee.com.cn/'
+        // baseURL = 'https://xavi.c1.sidoc.cn'
     }
 }
 export default {

+ 2 - 1
src/views/Hpp.vue

@@ -1,7 +1,8 @@
 <template>
   <div>
+	<s-header :name="$t('airwallex.sunzee')" :noback="false"></s-header>
     <h2>申泽智能</h2>
-    <button @click="redirectHpp()">Redirect to HPP</button>
+    <button @click="redirectHpp()">去付款Redirect to HPP</button>
   </div>
 </template>
 

+ 280 - 0
src/views/airwallex/beneficiary.vue

@@ -0,0 +1,280 @@
+<template>
+    <div class="airwallexPayment">
+        <s-header :name="$t('airwallex.payment')" :noback="false"></s-header>
+        <br>
+        <div class="form-container">
+            <div id="beneficiary-form-container" class="beneficiary-form-container" style="height: 100%;"></div>
+        </div>
+        <div class="button-container" v-if="showButton">
+            <van-button type="primary" class="cancel" @click="handleCancel">取消</van-button>
+            <van-button id="submit-button" type="primary" class="register" @click="handleSubmit">Submit</van-button>
+        </div>
+    </div>
+</template>
+  
+<script>
+import sHeader from "@/components/SimpleHeader";
+import { init, createElement } from '@airwallex/payouts-web-sdk';
+import { Base64 } from 'js-base64';
+import { getAuthCode, createBeneficiary } from '@/service/airwallex/index';
+import { AIRWALLEX_ADMIN_CLIENTID } from '@/constants/const.js';
+import { onMounted, ref } from 'vue';
+import { useRouter } from 'vue-router';
+import { styleUrl } from '../../common/js/utils';
+
+export default {
+    components: { sHeader },
+    setup() {
+        const router = useRouter();
+        const ready = ref(false);
+        let beneficiaryComponent;
+        const showButton = ref(false);
+        // const beneficiaryComponent = ref(null);
+
+        onMounted(async () => {
+            try {
+                // 生成 code_verifier
+                const codeVerifier = generateCodeVerifier();
+
+                // 生成 code_challenge
+                const codeChallengeStr = await generateCodeChallengeFromVerifier(codeVerifier);
+
+                // 生成 getAuthCode 接口的参数
+                const getAuthCodeDTO = {
+                    code_challenge: codeChallengeStr,
+                    scope: [
+                        // onboarding component
+                        // "w:awx_action:onboarding"
+                        // payout or beneficiary component
+                        "w:awx_action:transfers_edit"
+                    ]
+                };
+
+                console.log("getAuthCodeDTO", getAuthCodeDTO);
+
+                // 调用后端 getAuthCode 接口获取 authCode
+                const authCodeResponse = await getAuthCode(getAuthCodeDTO);
+                const authCodeStr = authCodeResponse.data.authorization_code;
+
+                await init({
+                    langKey: 'en',
+                    env: 'demo',
+                    authCode: authCodeStr,
+                    clientId: AIRWALLEX_ADMIN_CLIENTID,
+                    codeVerifier: codeVerifier,
+                });
+
+                // 调用 SDK 的 createElement 方法创建付款交易单/收款方模块
+                beneficiaryComponent = await createElement(eleType);
+
+                beneficiaryComponent.mount('#beneficiary-form-container');
+
+                beneficiaryComponent.on('ready', () => {
+                    ready.value = true;
+                    // 添加组件渲染后的逻辑
+                    console.log('组件呈现后的逻辑...');
+                });
+                showButton.value = true;
+            } catch (error) {
+                console.error(error);
+                // 处理异常情况
+            }
+        });
+
+        const handleCancel = async () => {
+            router.push({
+                path: '/airwallex',
+            })
+        }
+
+        const handleSubmit = async () => {
+            if (ready.value) {
+                const formResult = await beneficiaryComponent.submit();
+                // Handle form results
+                console.log('submit 执行结果:', formResult);
+
+                // 提取收款人信息字段
+                const { beneficiary, nickname, payment_methods } = formResult.values;
+                const { company_name, entity_type, address, bank_details } = beneficiary;
+
+                // 验证收款人信息是否完整
+                if (!payment_methods || !entity_type || !address || !bank_details || !company_name) {
+                    throw new Error('收款人信息不完整');
+                }
+                
+                // 地址信息
+                const {
+                    city,
+                    country_code,
+                    postcode,
+                    state,
+                    street_address
+                } = address;
+                console.log("地址信息》》》", address);
+                // 验证地址信息是否完整
+                if (
+                    // !city ||
+                    !country_code ||
+                    // !postcode ||
+                    // !state ||
+                    !street_address
+                ) {
+                    throw new Error('地址信息不完整');
+                }
+
+                // 银行信息
+                const {
+                    account_currency,
+                    account_name,
+                    account_number,
+                    account_routing_type1,
+                    account_routing_value1,
+                    bank_country_code,
+                    bank_name,
+                    swift_code,
+                } = bank_details;
+                console.log("银行信息》》》", bank_details);
+                // 验证银行详细信息是否完整
+                if (
+                    !account_currency ||
+                    !account_name ||
+                    !account_number ||
+                    !bank_country_code ||
+                    !bank_name ||
+                    !swift_code
+                ) {
+                    throw new Error('银行详细信息不完整');
+                }
+
+                // 构建请求数据对象
+                const caBeneficiaryReq = {
+                    beneficiary: {
+                        company_name: company_name,
+                        entity_type: entity_type,
+                        address: {
+                            city: city,
+                            country_code: country_code,
+                            postcode: postcode,
+                            state: state,
+                            street_address: street_address,
+                        },
+                        bank_details: {
+                            account_currency: account_currency,
+                            account_name: account_name,
+                            account_number: account_number,
+                            account_routing_type1: account_routing_type1,
+                            account_routing_value1: account_routing_value1,
+                            bank_country_code: bank_country_code,
+                            bank_name: bank_name,
+                            swift_code: swift_code,
+                        },
+                    },
+                    nickname: nickname,
+                    payment_methods: payment_methods,
+                };
+
+                // const { field1, field2, field3 }  = formResult;
+                // let caBeneficiaryReq = {};
+
+                // caBeneficiaryReq.beneficiary = {
+                //     companyName: formResult.values.beneficiary.company_name,
+                //     entityType: formResult.values.beneficiary.entity_type,
+                // }
+                // caBeneficiaryReq.nickname = formResult.values.nickname;
+                // caBeneficiaryReq.paymentMethods = formResult.values.payment_methods;
+
+                // caBeneficiaryReq.beneficiary.address = {
+                //     city: formResult.values.beneficiary.address.city,
+                //     countryCode: formResult.values.beneficiary.address.country_code,
+                //     postcode: formResult.values.beneficiary.address.postcode,
+                //     state: formResult.values.beneficiary.address.state,
+                //     streetAddress: formResult.values.beneficiary.address.street_address,
+                // }
+                // caBeneficiaryReq.beneficiary.bankDetails = {
+                //     accountCurrency: formResult.values.beneficiary.bank_details.account_currency,
+                //     accountName: formResult.values.beneficiary.bank_details.account_name,
+                //     accountNumber: formResult.values.beneficiary.bank_details.account_number,
+                //     accountRoutingType1: formResult.values.beneficiary.bank_details.account_routing_type1,
+                //     accountRoutingValue1: formResult.values.beneficiary.bank_details.account_routing_value1,
+                //     bankCountryCode: formResult.values.beneficiary.bank_details.bank_country_code,
+                //     bankName: formResult.values.beneficiary.bank_details.bank_name,
+                //     swiftCode: formResult.values.beneficiary.bank_details.swift_code,
+                // }
+
+                // 请求后端接口 caBeneficiary
+                const respResult = await createBeneficiary(caBeneficiaryReq);
+                const beneficiaryId = respResult.data.beneficiary_id;
+                console.log("收款人id:", beneficiaryId);
+
+                // if 成功,将 beneficiary_id 缓存下来,跳转到付款单页面
+                if (beneficiaryId) {
+                    router.push({
+                        path: '/airwallexPayout',
+                        query: { beneficiaryId: beneficiaryId }
+                    })
+                }    
+
+
+                // 收款人发起付款请求,调用后端创建付款接口
+                // const paymentId = await createPayment(caPaymentDTO);
+                // console.log("付款交易id:", paymentId);
+
+                // if (paymentId) {
+                //     router.push({
+                //         path: '/paymentDetail',
+                //         query: { paymentId }
+                //     });
+                // }
+            }
+        };
+
+
+        // 生成 code_verifier
+        const dec2hex = (dec) => {
+            return ('0' + dec.toString(16)).slice(-2);
+        };
+
+        const generateCodeVerifier = () => {
+            // 生成 random length for code_verifier which should be between 43 and 128
+            const length = Math.random() * (129 - 43) + 43;
+            const array = new Uint32Array(length / 2);
+            window.crypto.getRandomValues(array);
+
+            return Array.from(array, dec2hex).join('');
+        };
+
+
+        // 生成 code_challenge
+        const sha256 = (plain) => {
+            const encoder = new TextEncoder();
+            const data = encoder.encode(plain);
+            return window.crypto.subtle.digest('SHA-256', data);
+        };
+
+        const base64urlencode = (hashed) => {
+            const bytes = new Uint8Array(hashed);
+            const base64encoded = Base64.fromUint8Array(bytes, true);
+            return base64encoded;
+        };
+
+        const generateCodeChallengeFromVerifier = async (codeVerifier) => {
+            const hashed = await sha256(codeVerifier);
+            const base64encoded = base64urlencode(hashed);
+            return base64encoded;
+        };
+
+        // type 字段:payoutForm(可选),beneficiaryForm
+        const eleType = "beneficiaryForm";
+
+        styleUrl("airwallex");
+
+        return {
+            ready,
+            handleSubmit,
+            showButton,
+            handleCancel
+        }
+    }
+}
+
+</script>

+ 201 - 0
src/views/airwallex/caPayment.vue

@@ -0,0 +1,201 @@
+<template>
+    <div class="airwallexPayment">
+        <s-header :name="$t('airwallex.payout')" :noback="false"></s-header>
+        <br>
+        <!-- 卡片展示付款信息 -->
+        <van-cell-group inset>
+            <van-cell title="您支付" :value="payment_amount + ' ' + source_currency" />
+            <van-cell title="付款日期" :value="payment_date" label="送达需要0~3个工作日" />
+            <!-- <van-cell title="收款方收到" :value="payment_amount + ' ' + payment_currency" /> -->
+        </van-cell-group>
+
+        <br>
+        <!-- 付款原因 -->
+        <van-cell-group inset>
+            <van-field v-model="reason" required is-link readonly label="付款原因" placeholder="选择付款原因" @click="showPicker = true" />
+            <van-popup v-model:show="showPicker" round position="bottom">
+                <van-picker :columns="columns" @cancel="showPicker = false" @confirm="onConfirm" />
+            </van-popup>
+        </van-cell-group>
+
+        <br>
+        <!-- 交易附言 -->
+        <van-cell-group inset>
+            <van-field v-model="reference" required label="交易附言" placeholder="此信息可能会展示在收款方银行账单上,帮助收款方甄别此付款。" :rules="[{ required: true, message: '此项为必填项'}]" />
+        </van-cell-group>
+
+        <br>
+        <!-- 备注(选填) -->
+        <van-cell-group inset>
+            <van-field v-model="text" label="备注" placeholder="选填:你可以通过它给审批人或其他用户提供额外信息。" />
+        </van-cell-group>
+
+        <div class="button-container">
+            <van-button type="primary" class="cancel" @click="handleCancel">取消</van-button>
+            <van-button type="primary" class="register" @click="handleSubmit">提交付款</van-button>
+        </div>
+    </div>
+</template>
+<script>
+import sHeader from "@/components/SimpleHeader";
+import { styleUrl } from '../../common/js/utils';
+import { useRouter, useRoute } from 'vue-router';
+import { ref, onMounted } from 'vue';
+import { createPayment } from '@/service/airwallex/index';
+
+
+export default {
+    components: { sHeader },
+    setup() {
+
+        const router = useRouter();
+        const route = useRoute();
+        let caPaymentReq;
+
+        const source_amount = ref();
+        const source_currency = ref();
+        const payment_date = ref();
+        const payment_currency = ref();
+        const payment_amount = ref();
+        const payment_method = ref();
+        const beneficiary_id = ref();
+
+        const reference = ref('');
+        const regerenceError = ref(false);
+
+
+        const columns = [
+            'wages_salary',
+            'donation_charitable_contribution',
+            'personal_remittance',
+            'transfer_to_own_account',
+            'pension',
+            'family_support',
+            'living_expenses',
+            'education_training',
+            'travel',
+            'investment_proceeds',
+            'investment_capital',
+            'loan_credit_repayment',
+            'taxes',
+            'goods_purchased',
+            'business_expenses',
+            'medical_services',
+            'professional_business_services',
+            'technical_services',
+            'other_services',
+            'construction',
+            'freight',
+            'real_estate',
+            'audio_visual_services'
+        ];
+
+        const reason = ref('');
+        const showPicker = ref(false);
+
+        const onConfirm = (selectedItem) => {
+            reason.value = selectedItem;
+            showPicker.value = false;
+        };
+
+        const handleCancel = async () => {
+            router.push({
+                path: '/airwallex',
+                query: {
+
+                }
+            })
+        }
+
+        onMounted(async () => {
+            try {
+                // 获取从 payout 页面获取到的参数
+                // const beneficiaryId = router.currentRoute.value.query.beneficiaryId;
+                // const payment_currency = router.currentRoute.value.query.payment_currency;
+                // const payment_method = router.currentRoute.value.query.payment_method;
+                // const source_currency = router.currentRoute.value.query.source_currency;
+                // const source_amount = router.currentRoute.value.query.source_amount;
+                // const fee_paid_by = router.currentRoute.value.query.fee_paid_by;
+                // const payment_date = router.currentRoute.value.query.payment_date;
+
+                beneficiary_id.value = route.query.beneficiary_id;
+                payment_currency.value = route.query.payment_currency;
+                payment_method.value = route.query.payment_method;
+                source_currency.value = route.query.source_currency;
+
+                payment_date.value = route.query.payment_date;
+                // const fee_paid_by = route.query.fee_paid_by;
+
+                // 注:这里的 payment_amount 用的是 source_amount
+                // source_amount.value = route.query.source_amount;
+                payment_amount.value = route.query.source_amount;
+
+                console.log("beneficiary_id >>> " + beneficiary_id.value);
+                console.log("payment_currency >>> " + payment_currency.value);
+                console.log("payment_method >>> " + payment_method.value);
+                console.log("source_currency >>> " + source_currency.value);
+                console.log("payment_amount >>> " + payment_amount.value);
+
+            } catch (error) {
+                console.error(error);
+                // 处理异常情况
+            }
+        });
+
+        const handleSubmit = async () => {
+            if (caPaymentReq !== null) {
+
+                if(reference.value === '') {
+                    regerenceError.value = true;
+                    return;
+                }
+                console.log("reference:::", reference.value);
+                console.log("reason:::", reason.value);
+
+                const caPaymentReq = {
+                    beneficiary_id: beneficiary_id.value,
+                    payment_currency: payment_currency.value,
+                    payment_method: payment_method.value,
+                    reason: reason.value,
+                    reference: reference.value,
+                    // request_id: request_id,
+                    source_currency: source_currency.value,
+                    payment_amount: payment_amount.value
+                }
+
+                // 请求后端接口
+                const resultsResp = await createPayment(caPaymentReq);
+                console.log("resultResp>>>", resultsResp);
+                console.log("paymentId>>>", resultsResp.data);
+
+                if(resultsResp) {
+                    router.push({
+                        path: '/paymentDetail',
+                        query: {
+                            paymentId: resultsResp.data,
+                        }
+                    })
+                }
+            }
+        }
+
+
+        styleUrl("airwallex");
+        return {
+            handleCancel,
+            handleSubmit,
+            source_amount,
+            source_currency,
+            payment_date,
+            payment_currency,
+            payment_amount,
+            reason,
+            columns,
+            onConfirm,
+            showPicker,
+            reference
+        }
+    }
+}
+
+</script>

+ 10 - 1
src/views/airwallex/index.vue

@@ -6,6 +6,15 @@
                 <van-cell center :title="$t('airwallex.amount')" :value="amount" label="描述信息:可提取的金额" />
             </van-cell-group>
         </div>
+        <br>
+        <div class="cell-container">
+            <van-cell-group inset>
+                <van-cell title="创建收款单" is-link to="/airwallexPayout" />
+                <van-cell title="付款详情页" is-link to="/paymentDetail" />
+                <van-cell title="去百度" is-link url="https://www.baidu.com" />
+            </van-cell-group>
+        </div>
+        <br>
         <div class="airwallexWalletBtn">
             <van-button round type="primary" class="register" @click="handleWithdraw">提现</van-button>
         </div>
@@ -27,7 +36,7 @@ export default {
         const handleWithdraw = () => {
 
             // 导航到提现页面
-            router.push('/airwallexPayment');
+            router.push('/airwallexBeneficiary');
         };
 
         return {

+ 0 - 148
src/views/airwallex/payment.vue

@@ -1,148 +0,0 @@
-<template>
-    <div class="airwallexPayment">
-        <s-header :name="$t('airwallex.payment')" :noback="false"></s-header>
-        <div id="container"></div>
-    </div>
-</template>
-  
-<script>
-import { init, createElement } from '@airwallex/payouts-web-sdk';
-import { Base64 } from 'js-base64';
-import { getAuthCode } from '@/service/airwallex/index';
-import { styleUrl } from '../../common/js/utils';
-import sHeader from "@/components/SimpleHeader";
-import { ref, onMounted } from 'vue';
-
-export default {
-    components: { sHeader },
-    setup() {
-        styleUrl("airwallex")
-        const ready = ref(false);
-        const element = ref(null);
-
-        onMounted(async () => {
-            // 生成 code_verifier 和 code_challenge
-            const codeVerifier = generateCodeVerifier();
-            const codeChallenge = await generateCodeChallengeFromVerifier(codeVerifier);
-
-            // 发送请求获取 AuthCode
-            const response = await getAuthCodeFun(codeChallenge);
-            // 初始化 SDK
-            await init({
-                langKey: 'en',
-                env: 'Staging',
-                authCode: response.authCode,
-                clientId: 'IBi-pVO4SSGzXIDHRYfVOA',
-                codeVerifier: codeVerifier,
-            });
-
-            element.value = createElement(type, options);
-            element.value.mount('#container');
-
-            element.value.on('ready', () => {
-                ready.value = true;
-                // 在组件准备就绪后处理逻辑
-            });
-
-            element.value.on('change', ({ value }) => {
-                // 处理表单值变化事件
-                console.log(value);
-            });
-
-            element.value.on('formState', ({ loading, validating, errors }) => {
-                // 处理表单状态变化事件;
-                console.log(loading, validating, errors);
-            });
-        });
-        
-
-        const handleFormSubmit = async () => {
-            if (ready.value) {
-                const formResult = await element.value.submit();
-                // 处理表单结果
-                console.log(formResult);
-            }
-        };
-
-        // Generate 生成 code_verifier
-        const dec2hex = (dec) => {
-            return ('0' + dec.toString(16)).slice(-2);
-        };
-
-        const generateCodeVerifier = () => {
-            // generate random length for code_verifier which should be between 43 and 128
-            // 生成长度介于43到128之间的随机长度的 code_verifier
-            const length = Math.random() * (129 - 43) + 43;
-            const array = new Uint32Array(length / 2);
-            window.crypto.getRandomValues(array);
-
-            return Array.from(array, dec2hex).join('');
-        };
-
-        const sha256 = (plain) => {
-            const encoder = new TextEncoder();
-            const data = encoder.encode(plain);
-            return window.crypto.subtle.digest('SHA-256', data);
-        };
-
-        const base64urlencode = (hashed) => {
-            const bytes = new Uint8Array(hashed);
-            const base64encoded = Base64.fromUint8Array(bytes, true);
-            return base64encoded;
-        };
-
-        const generateCodeChallengeFromVerifier = async (codeVerifier) => {
-            const hashed = await sha256(codeVerifier);
-            const base64encoded = base64urlencode(hashed);
-            return base64encoded;
-        };
-
-        const getAuthCodeFun = async (codeChallenge) => {
-            // 发送请求获取 AuthCode
-            const getAuthCodeDTO = {
-                codeChallenge: codeChallenge,
-                scope: [
-                    "w:awx_action:onboarding"
-                ]
-            };
-            const response = await getAuthCode(getAuthCodeDTO);
-            return response.data;
-        };
-
-
-        // type 字段:payoutForm,beneficiaryForm
-        const type = "payoutForm";
-        // options 字段:非必须
-        const options = {
-            // 设置默认字段值(可选)
-            defaultValues: {
-                field1: 'value1',
-                field2: 'value2',
-                // ...
-            },
-            // 自定义组件样式和行为(可选)
-            customizations: {
-                // 自定义样式
-                styles: {
-                    // ...
-                },
-                // 自定义字段验证逻辑
-                fieldValidators: {
-                    // ...
-                },
-                // ...
-            },
-            // 设置组件的主题(可选)
-            theme: {
-                // ...
-            },
-        };
-
-
-        return {
-            element,
-            handleFormSubmit
-        };
-    },
-};
-</script>

+ 54 - 0
src/views/airwallex/paymentDetail.vue

@@ -0,0 +1,54 @@
+<template>
+    <div>
+        <s-header :name="$t('airwallex.paymentDetail')" :noback="false"></s-header>
+        <div>付款详情:</div>
+        
+        <div class="button-container">
+            <van-button round type="primary" @click="backToIndex">关闭</van-button>
+        </div>
+    </div>
+</template>
+  
+<script>
+import sHeader from "@/components/SimpleHeader";
+import { styleUrl } from '../../common/js/utils';
+import { useRouter, useRoute } from "vue-router";
+import { ref, onMounted } from 'vue';
+
+
+export default {
+    setup() {
+
+        const router = useRouter();
+        const route = useRoute();
+        const paymentId = ref();
+
+
+        styleUrl("airwallex");
+
+        onMounted(async () => {
+            try {
+                // 获取从 payout 页面获取到的参数
+                paymentId.value = route.query.paymentId;
+
+                console.log("paymentId >>> " + paymentId.value);
+
+            } catch (error) {
+                console.error(error);
+                // 处理异常情况
+            }
+        });
+
+
+        // 导航到 airwallex 页面
+        const backToIndex = async () => {
+            router.push('/airwallex');
+        }
+        return {
+            backToIndex
+        };
+    },
+    components: { sHeader },
+};
+</script>
+  

+ 279 - 0
src/views/airwallex/payout.vue

@@ -0,0 +1,279 @@
+<template>
+    <div class="airwallexPayment">
+        <s-header :name="$t('airwallex.payout')" :noback="false"></s-header>
+        <br>
+        <div class="form-container">
+            <div id="payout-form-container" class="payout-form-container" style="height: 100%;"></div>
+        </div>
+        <div class="button-container">
+            <van-button type="primary" class="cancel" @click="handleCancel">取消</van-button>
+            <van-button type="primary" class="register" @click="handleSubmit">继续</van-button>
+        </div>
+    </div>
+</template>
+
+<script>
+import sHeader from "@/components/SimpleHeader";
+import { init, createElement } from '@airwallex/payouts-web-sdk';
+import { Base64 } from 'js-base64';
+import { AIRWALLEX_ADMIN_CLIENTID } from '@/constants/const.js';
+import { getAuthCode } from '@/service/airwallex/index';
+import { onMounted, ref } from 'vue';
+import { styleUrl } from '../../common/js/utils';
+import { useRouter, useRoute } from 'vue-router';
+
+
+export default {
+    components: { sHeader },
+    setup() {
+        const router = useRouter();
+        const route = useRoute();
+        const ready = ref(false);
+        let payoutComponent;
+
+        onMounted(async () => {
+            try {
+                // 获取从 beneficiary 页面获取到的 beneficiaryId
+                // const beneficiaryId = router.currentRoute.value.query.beneficiaryId;
+                const beneficiaryId = route.query.beneficiaryId;
+                console.log("beneficiaryId >>> " + beneficiaryId);
+
+                // generate code_verifier
+                const codeVerifier = generateCodeVerifier();
+
+                // generate code_challenge
+                const codeChallengeStr = await generateCodeChallengeFromVerifier(codeVerifier);
+
+                // getAuthCode 接口参数
+                const getAuthCodeDTO = {
+                    code_challenge: codeChallengeStr,
+                    scope: [
+                        "w:awx_action:transfers_edit"
+                    ]
+                };
+
+                console.log("getAuthCodeDTO", getAuthCodeDTO);
+
+                // generate authCode
+                const authCodeResponse = await getAuthCode(getAuthCodeDTO);
+                const authCodeStr = authCodeResponse.data.authorization_code;
+
+                await init({
+                    langKey: 'en',
+                    env: 'demo',
+                    authCode: authCodeStr,
+                    clientId: AIRWALLEX_ADMIN_CLIENTID,
+                    codeVerifier: codeVerifier,
+                });
+
+                // 调用 SDK 的 createElement 方法创建付款交易单/收款方模块
+                payoutComponent = await createElement(eleType, {
+                    defaultValues: {
+                        beneficiary_id: beneficiaryId,
+                        // payment_currency: 'USD',
+                    }
+                });
+
+                payoutComponent.mount('#payout-form-container');
+
+                payoutComponent.on('ready', () => {
+                    ready.value = true;
+                    // 添加组件渲染后的逻辑
+                    console.log('组件呈现后的逻辑...');
+                });
+            } catch (error) {
+                console.error(error);
+                // 处理异常情况
+            }
+        });
+
+        const handleCancel = async () => {
+            router.push({
+                path: '/airwallex',
+            })
+        }
+
+        const handleSubmit = async () => {
+            if (ready.value) {
+                const formResult = await payoutComponent.submit();
+                // Handle form results
+                console.log('submit 执行结果:', formResult);
+
+                const {
+                    values
+                } = formResult;
+
+                const {
+                    beneficiary_id,
+                    payment_currency,
+                    payment_method,
+                    source_currency,
+                    source_amount,
+                    fee_paid_by,
+                    payment_date,
+                } = values;
+
+                // 校验 values 参数
+                if (
+                    !beneficiary_id ||
+                    !payment_currency ||
+                    !payment_method ||
+                    !source_currency
+                    // !reason ||
+                    // !reference ||
+                ) {
+                    throw new Error('缺少必要参数');
+                }
+
+                if (formResult) {
+                    router.push({
+                        path: '/caPayment',
+                        query: {
+                            beneficiary_id: beneficiary_id,
+                            payment_currency: payment_currency,
+                            payment_method: payment_method,
+                            // reason: reason,
+                            // reference: reference,
+                            source_currency: source_currency,
+                            // request_id: request_id,
+                            source_amount: source_amount,
+                            fee_paid_by: fee_paid_by,
+                            payment_date: payment_date,
+                        }
+                    })
+                }
+            }
+        }
+
+        // const handleSubmit = async () => {
+        //     if (ready.value) {
+        //         const formResult = await payoutComponent.submit();
+        //         // Handle form results
+        //         console.log('submit 执行结果:', formResult);
+
+        //         const {
+        //             values, additionalInfo
+        //         } = formResult;
+
+        //         const {
+        //             beneficiary_id,
+        //             source_currency,
+        //             payment_method,
+        //             payment_currency,
+        //             // source_amount,
+        //             // fee_paid_by,
+        //             // payment_date,
+        //         } = values;
+
+
+        //         // 校验 values 参数
+        //         if (
+        //             !beneficiary_id ||
+        //             !payment_currency ||
+        //             !payment_method ||
+        //             // !reason ||
+        //             // !reference ||
+        //             !source_currency
+        //         ) {
+        //             throw new Error('缺少必要参数');
+        //         }
+
+        //         // const reason = ref(additionalInfo.reason);
+
+        //         // 
+        //         // setTimeout(() => {
+        //         //     additionalInfo.value = {
+        //         //         reason: {
+        //         //             label: "Audiovisual services",
+        //         //             value: "audio_visual_services"
+        //         //         },
+        //         //         // 其他理由...
+        //         //     }
+        //         // }, 1000)
+
+        //         // const reason = additionalInfo.value.additionalInfo
+        //         // const regference = additionalInfo
+
+        //         // 把这些参数都带去新的页面,然后再去新的页面获取reason 和 sdfsdfs,最后再去请问
+
+        //         // 构建请求数据对象
+        //         // 后端必须的参数 
+        //         // beneficiary_id, 
+        //         // payment_currency, 
+        //         // payment_method, 
+        //         // reason, 
+        //         // reference, 
+        //         // request_id, 
+        //         // source_currency
+        //         const caPaymentReq = {
+        //             beneficiary_id: beneficiary_id,
+        //             payment_currency: payment_currency,
+        //             payment_method: payment_method,
+        //             // reason: reason,
+        //             // reference: reference,
+        //             // request_id: request_id,
+        //             source_currency: source_currency
+        //         }
+
+        //         // 请求后端接口
+        //         const resultsResp = await createPayment(caPaymentReq);
+        //         console.log("resultResp>>>", resultsResp);
+
+        //         // if 成功,将 
+        //         // if (resultsResp.code === "00000") {
+        //         //     router.push({
+        //         //         path: '/caPayment',
+        //         //         query: { beneficiaryId: beneficiaryId }
+        //         //     })
+        //         // }
+
+        //     }
+        // };
+
+
+        // 生成 code_verifier
+        const dec2hex = (dec) => {
+            return ('0' + dec.toString(16)).slice(-2);
+        };
+
+        const generateCodeVerifier = () => {
+            // 生成 random length for code_verifier which should be between 43 and 128
+            const length = Math.random() * (129 - 43) + 43;
+            const array = new Uint32Array(length / 2);
+            window.crypto.getRandomValues(array);
+
+            return Array.from(array, dec2hex).join('');
+        };
+
+
+        // 生成 code_challenge
+        const sha256 = (plain) => {
+            const encoder = new TextEncoder();
+            const data = encoder.encode(plain);
+            return window.crypto.subtle.digest('SHA-256', data);
+        };
+
+        const base64urlencode = (hashed) => {
+            const bytes = new Uint8Array(hashed);
+            const base64encoded = Base64.fromUint8Array(bytes, true);
+            return base64encoded;
+        };
+
+        const generateCodeChallengeFromVerifier = async (codeVerifier) => {
+            const hashed = await sha256(codeVerifier);
+            const base64encoded = base64urlencode(hashed);
+            return base64encoded;
+        };
+
+        // type 字段:payoutForm(可选),beneficiaryForm
+        const eleType = "payoutForm";
+
+        styleUrl("airwallex");
+        return {
+            ready,
+            handleSubmit,
+            handleCancel
+        }
+    }
+}
+</script>