mirror of
https://gitee.com/bootx/dax-pay-ui
synced 2026-08-11 23:25:33 +08:00
feat(admin): 易支付配置页与应用工作台入口
新增 EasyPayConfig 子页(两列分区、V1/V2 显隐、查看/编辑),工作台支付配置入口与权限码;平台公钥展示去 PEM 纯 Base64,补齐多语言菜单与文案。
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
import type { BaseEntity, Result } from '#/types/web';
|
||||
|
||||
import { defHttp } from '#/api/request';
|
||||
|
||||
/**
|
||||
* 易支付凭证 API(应用级)
|
||||
*/
|
||||
export const EasyPayCredentialApi = {
|
||||
/**
|
||||
* 按应用号查询(不存在则自动创建)
|
||||
*/
|
||||
getByAppId(appId: string): Promise<Result<EasyPayCredentialResult>> {
|
||||
return defHttp.get({ url: '/admin/easypay/credential/get-by-app-id', params: { appId } });
|
||||
},
|
||||
/**
|
||||
* 更新凭证
|
||||
*/
|
||||
update(data: EasyPayCredentialParam): Promise<Result<void>> {
|
||||
return defHttp.post({ url: '/admin/easypay/credential/update', data });
|
||||
},
|
||||
};
|
||||
|
||||
export interface EasyPayCredentialResult extends BaseEntity {
|
||||
pid?: number;
|
||||
appId?: string;
|
||||
enable?: boolean;
|
||||
enableV1?: boolean;
|
||||
enableV2?: boolean;
|
||||
md5Key?: string;
|
||||
useSystemKey?: boolean;
|
||||
publicKey?: string;
|
||||
platformPublicKey?: string;
|
||||
easyPayV1ApiUrl?: string;
|
||||
easyPayV2ApiUrl?: string;
|
||||
}
|
||||
|
||||
export interface EasyPayCredentialParam {
|
||||
id?: string;
|
||||
appId: string;
|
||||
enable?: boolean;
|
||||
enableV1?: boolean;
|
||||
enableV2?: boolean;
|
||||
md5Key?: string;
|
||||
useSystemKey?: boolean;
|
||||
publicKey?: string;
|
||||
}
|
||||
@@ -71,6 +71,11 @@ export const PermCodes = {
|
||||
VIEW: 'merchant:gateway-cashier:view',
|
||||
MANAGE: 'merchant:gateway-cashier:manage',
|
||||
},
|
||||
/** 易支付协议配置 menuCode=merchant:easypay */
|
||||
EasyPay: {
|
||||
VIEW: 'merchant:easypay:view',
|
||||
MANAGE: 'merchant:easypay:manage',
|
||||
},
|
||||
Store: {
|
||||
VIEW: 'merchant:store:view',
|
||||
MANAGE: 'merchant:store:manage',
|
||||
|
||||
@@ -47,5 +47,38 @@
|
||||
"deleteDesc": "This cannot be undone. Proceed with caution",
|
||||
"noActions": "No actions available",
|
||||
"openActionAria": "Open app configuration"
|
||||
},
|
||||
"easypay": {
|
||||
"v1Hint": "V1 is weakly secured; enable only for legacy clients",
|
||||
"useSystemKey": "Use system key pair",
|
||||
"v2Url": "V2 API base URL",
|
||||
"desc": "Expose EasyPay V1/V2 protocol APIs",
|
||||
"title": "EasyPay Config",
|
||||
"notifyHint": "Async notify is not sent in this release; poll query API",
|
||||
"platformPublicKey": "Platform RSA public key",
|
||||
"enableV1": "Enable V1",
|
||||
"confirmSave": "Save EasyPay configuration?",
|
||||
"md5Key": "V1 MD5 key",
|
||||
"pid": "EasyPay merchant id (pid)",
|
||||
"enableV2": "Enable V2",
|
||||
"md5KeyPh": "Enter MD5 key",
|
||||
"publicKey": "Merchant RSA public key",
|
||||
"v1Url": "V1 API base URL",
|
||||
"enable": "Enable",
|
||||
"section": {
|
||||
"endpoint": "Basics",
|
||||
"v1": "V1 protocol",
|
||||
"v2": "V2 protocol"
|
||||
},
|
||||
"pidDesc": "App-assigned EasyPay merchant id used as pid by integrators",
|
||||
"v2UrlDesc": "V2 protocol API base URL to share with integrators",
|
||||
"v1UrlDesc": "V1 protocol API base URL for legacy clients only",
|
||||
"enableDesc": "When off, this app rejects EasyPay protocol requests",
|
||||
"enableV2Desc": "Recommended; use V2 for new integrations",
|
||||
"enableV1Desc": "V1 is weakly secured; enable only for legacy clients",
|
||||
"md5KeyDesc": "MD5 key used for V1 request signing",
|
||||
"useSystemKeyDesc": "Use the platform key pair so merchant public key is not required",
|
||||
"publicKeyDesc": "Merchant RSA public key when using a custom key pair",
|
||||
"platformPublicKeyDesc": "Platform public key for integrator signature verification"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -47,5 +47,38 @@
|
||||
"deleteDesc": "Hal ini tidak dapat dibatalkan. Lanjutkan dengan hati-hati",
|
||||
"noActions": "Tidak ada tindakan yang tersedia",
|
||||
"openActionAria": "Buka konfigurasi aplikasi"
|
||||
},
|
||||
"easypay": {
|
||||
"v1Hint": "V1 memiliki keamanan lemah; aktifkan hanya untuk klien lama",
|
||||
"useSystemKey": "Gunakan pasangan kunci sistem",
|
||||
"v2Url": "URL dasar API V2",
|
||||
"desc": "Menyediakan API protokol EasyPay V1/V2",
|
||||
"title": "Konfigurasi EasyPay",
|
||||
"notifyHint": "Notifikasi async tidak dikirim pada rilis ini; lakukan polling API query",
|
||||
"platformPublicKey": "Kunci publik RSA platform",
|
||||
"enableV1": "Aktifkan V1",
|
||||
"confirmSave": "Simpan konfigurasi EasyPay?",
|
||||
"md5Key": "Kunci MD5 V1",
|
||||
"pid": "ID merchant EasyPay (pid)",
|
||||
"enableV2": "Aktifkan V2",
|
||||
"md5KeyPh": "Masukkan kunci MD5",
|
||||
"publicKey": "Kunci publik RSA merchant",
|
||||
"v1Url": "URL dasar API V1",
|
||||
"enable": "Aktifkan",
|
||||
"section": {
|
||||
"endpoint": "Dasar",
|
||||
"v1": "Protokol V1",
|
||||
"v2": "Protokol V2"
|
||||
},
|
||||
"pidDesc": "ID merchant EasyPay yang ditetapkan per aplikasi, digunakan sebagai pid oleh integrator",
|
||||
"v2UrlDesc": "URL dasar API protokol V2 untuk dibagikan ke integrator",
|
||||
"v1UrlDesc": "URL dasar API protokol V1, hanya untuk klien lama",
|
||||
"enableDesc": "Saat nonaktif, aplikasi ini menolak permintaan protokol EasyPay",
|
||||
"enableV2Desc": "Direkomendasikan; gunakan V2 untuk integrasi baru",
|
||||
"enableV1Desc": "V1 memiliki keamanan lemah; aktifkan hanya untuk klien lama",
|
||||
"md5KeyDesc": "Kunci MD5 untuk penandatanganan permintaan V1",
|
||||
"useSystemKeyDesc": "Gunakan pasangan kunci platform sehingga kunci publik merchant tidak diperlukan",
|
||||
"publicKeyDesc": "Kunci publik RSA merchant saat menggunakan pasangan kunci kustom",
|
||||
"platformPublicKeyDesc": "Kunci publik platform untuk verifikasi tanda tangan integrator"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,5 +47,38 @@
|
||||
"deleteDesc": "これを元に戻すことはできません。慎重に進めてください",
|
||||
"noActions": "利用可能なアクションはありません",
|
||||
"openActionAria": "アプリ設定を開く"
|
||||
},
|
||||
"easypay": {
|
||||
"v1Hint": "V1 は安全性が低いため、旧システム互換時のみ有効にしてください",
|
||||
"useSystemKey": "システム鍵ペアを使用",
|
||||
"v2Url": "V2 接続URL",
|
||||
"desc": "EasyPay V1/V2 プロトコルによる外部連携",
|
||||
"title": "EasyPay設定",
|
||||
"notifyHint": "本バージョンでは非同期通知を送信しません。query で注文状態をポーリングしてください",
|
||||
"platformPublicKey": "プラットフォーム RSA 公開鍵",
|
||||
"enableV1": "V1 を有効化",
|
||||
"confirmSave": "EasyPay 設定を保存しますか?",
|
||||
"md5Key": "V1 MD5 鍵",
|
||||
"pid": "EasyPay 加盟店番号 (pid)",
|
||||
"enableV2": "V2 を有効化",
|
||||
"md5KeyPh": "MD5 鍵を入力",
|
||||
"publicKey": "加盟店 RSA 公開鍵",
|
||||
"v1Url": "V1 接続URL",
|
||||
"enable": "有効",
|
||||
"section": {
|
||||
"endpoint": "基本接続",
|
||||
"v1": "V1 プロトコル設定",
|
||||
"v2": "V2 プロトコル設定"
|
||||
},
|
||||
"pidDesc": "アプリ単位で割り当てられる EasyPay 加盟店番号。連携側は pid として使用",
|
||||
"v2UrlDesc": "V2 プロトコル API のルートURL。連携側へ共有",
|
||||
"v1UrlDesc": "V1 プロトコル API のルートURL。旧システム互換時のみ",
|
||||
"enableDesc": "無効にすると当該アプリは EasyPay リクエストを拒否します",
|
||||
"enableV2Desc": "推奨。新規連携は V2 を利用してください",
|
||||
"enableV1Desc": "V1 は安全性が低いため、旧システム互換時のみ有効にしてください",
|
||||
"md5KeyDesc": "V1 署名用の MD5 鍵",
|
||||
"useSystemKeyDesc": "プラットフォーム鍵ペアを使う場合、加盟店公開鍵の入力は不要です",
|
||||
"publicKeyDesc": "独自鍵ペア利用時の加盟店 RSA 公開鍵",
|
||||
"platformPublicKeyDesc": "連携側の署名検証用プラットフォーム公開鍵"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -47,5 +47,38 @@
|
||||
"deleteDesc": "이 작업은 취소할 수 없습니다. 주의해서 진행하세요",
|
||||
"noActions": "사용 가능한 작업이 없습니다.",
|
||||
"openActionAria": "앱 구성 열기"
|
||||
},
|
||||
"easypay": {
|
||||
"v1Hint": "V1은 보안이 약하므로 레거시 호환 시에만 활성화하세요",
|
||||
"useSystemKey": "시스템 키 쌍 사용",
|
||||
"v2Url": "V2 연동 주소",
|
||||
"desc": "EasyPay V1/V2 프로토콜 외부 연동",
|
||||
"title": "EasyPay 설정",
|
||||
"notifyHint": "이번 버전에서는 비동기 알림을 보내지 않습니다. query로 주문 상태를 폴링하세요",
|
||||
"platformPublicKey": "플랫폼 RSA 공개키",
|
||||
"enableV1": "V1 사용",
|
||||
"confirmSave": "EasyPay 설정을 저장하시겠습니까?",
|
||||
"md5Key": "V1 MD5 키",
|
||||
"pid": "EasyPay 가맹점 번호(pid)",
|
||||
"enableV2": "V2 사용",
|
||||
"md5KeyPh": "MD5 키 입력",
|
||||
"publicKey": "가맹점 RSA 공개키",
|
||||
"v1Url": "V1 연동 주소",
|
||||
"enable": "사용",
|
||||
"section": {
|
||||
"endpoint": "기본 연동",
|
||||
"v1": "V1 프로토콜 설정",
|
||||
"v2": "V2 프로토콜 설정"
|
||||
},
|
||||
"pidDesc": "앱별로 할당된 EasyPay 가맹점 번호. 연동측에서 pid로 사용",
|
||||
"v2UrlDesc": "V2 프로토콜 API 루트 URL. 연동측에 공유",
|
||||
"v1UrlDesc": "V1 프로토콜 API 루트 URL. 레거시 호환 전용",
|
||||
"enableDesc": "끄면 해당 앱은 EasyPay 요청을 거부합니다",
|
||||
"enableV2Desc": "권장. 신규 연동은 V2를 사용하세요",
|
||||
"enableV1Desc": "V1은 보안이 약하므로 레거시 호환 시에만 활성화하세요",
|
||||
"md5KeyDesc": "V1 서명용 MD5 키",
|
||||
"useSystemKeyDesc": "플랫폼 키 쌍을 사용하면 가맹점 공개키를 입력할 필요가 없습니다",
|
||||
"publicKeyDesc": "자체 키 쌍 사용 시 가맹점 RSA 공개키",
|
||||
"platformPublicKeyDesc": "연동측 서명 검증용 플랫폼 공개키"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -47,5 +47,38 @@
|
||||
"deleteDesc": "Ini tidak boleh dibuat asal. Teruskan dengan berhati-hati",
|
||||
"noActions": "Tiada tindakan tersedia",
|
||||
"openActionAria": "Buka konfigurasi apl"
|
||||
},
|
||||
"easypay": {
|
||||
"v1Hint": "V1 mempunyai keselamatan yang lemah; aktifkan hanya untuk klien lama",
|
||||
"useSystemKey": "Gunakan pasangan kunci sistem",
|
||||
"v2Url": "URL asas API V2",
|
||||
"desc": "Mendedahkan API protokol EasyPay V1/V2",
|
||||
"title": "Konfigurasi EasyPay",
|
||||
"notifyHint": "Pemberitahuan async tidak dihantar dalam keluaran ini; tinjau API query",
|
||||
"platformPublicKey": "Kunci awam RSA platform",
|
||||
"enableV1": "Aktifkan V1",
|
||||
"confirmSave": "Simpan konfigurasi EasyPay?",
|
||||
"md5Key": "Kunci MD5 V1",
|
||||
"pid": "ID pedagang EasyPay (pid)",
|
||||
"enableV2": "Aktifkan V2",
|
||||
"md5KeyPh": "Masukkan kunci MD5",
|
||||
"publicKey": "Kunci awam RSA pedagang",
|
||||
"v1Url": "URL asas API V1",
|
||||
"enable": "Aktifkan",
|
||||
"section": {
|
||||
"endpoint": "Asas",
|
||||
"v1": "Protokol V1",
|
||||
"v2": "Protokol V2"
|
||||
},
|
||||
"pidDesc": "ID pedagang EasyPay yang ditetapkan setiap aplikasi, digunakan sebagai pid oleh pengintegrasi",
|
||||
"v2UrlDesc": "URL asas API protokol V2 untuk dikongsi dengan pengintegrasi",
|
||||
"v1UrlDesc": "URL asas API protokol V1, hanya untuk klien lama",
|
||||
"enableDesc": "Apabila dimatikan, aplikasi ini menolak permintaan protokol EasyPay",
|
||||
"enableV2Desc": "Disyorkan; gunakan V2 untuk integrasi baharu",
|
||||
"enableV1Desc": "V1 mempunyai keselamatan yang lemah; aktifkan hanya untuk klien lama",
|
||||
"md5KeyDesc": "Kunci MD5 untuk penandatanganan permintaan V1",
|
||||
"useSystemKeyDesc": "Gunakan pasangan kunci platform supaya kunci awam pedagang tidak diperlukan",
|
||||
"publicKeyDesc": "Kunci awam RSA pedagang apabila menggunakan pasangan kunci tersuai",
|
||||
"platformPublicKeyDesc": "Kunci awam platform untuk pengesahan tandatangan pengintegrasi"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,5 +47,38 @@
|
||||
"deleteDesc": "สิ่งนี้ไม่สามารถยกเลิกได้ ดำเนินการด้วยความระมัดระวัง",
|
||||
"noActions": "ไม่มีการดำเนินการใดๆ",
|
||||
"openActionAria": "เปิดการกำหนดค่าแอป"
|
||||
},
|
||||
"easypay": {
|
||||
"v1Hint": "V1 มีความปลอดภัยต่ำ; เปิดใช้งานเฉพาะไคลเอนต์เก่า",
|
||||
"useSystemKey": "ใช้คู่คีย์ของระบบ",
|
||||
"v2Url": "URL ฐาน API V2",
|
||||
"desc": "เปิดเผย API โปรโตคอล EasyPay V1/V2",
|
||||
"title": "การตั้งค่า EasyPay",
|
||||
"notifyHint": "รุ่นนี้ยังไม่ส่งการแจ้งเตือนแบบ async; ให้สอบถาม query API",
|
||||
"platformPublicKey": "รหัสสาธารณะ RSA ของแพลตฟอร์ม",
|
||||
"enableV1": "เปิดใช้งาน V1",
|
||||
"confirmSave": "บันทึกการตั้งค่า EasyPay?",
|
||||
"md5Key": "คีย์ MD5 V1",
|
||||
"pid": "รหัสผู้ค้า EasyPay (pid)",
|
||||
"enableV2": "เปิดใช้งาน V2",
|
||||
"md5KeyPh": "กรอกคีย์ MD5",
|
||||
"publicKey": "รหัสสาธารณะ RSA ของผู้ค้า",
|
||||
"v1Url": "URL ฐาน API V1",
|
||||
"enable": "เปิดใช้งาน",
|
||||
"section": {
|
||||
"endpoint": "พื้นฐาน",
|
||||
"v1": "โปรโตคอล V1",
|
||||
"v2": "โปรโตคอล V2"
|
||||
},
|
||||
"pidDesc": "รหัสผู้ค้า EasyPay ที่กำหนดให้แต่ละแอป ใช้เป็น pid โดยผู้เชื่อมต่อ",
|
||||
"v2UrlDesc": "URL ฐาน API โปรโตคอล V2 สำหรับแชร์ให้ผู้เชื่อมต่อ",
|
||||
"v1UrlDesc": "URL ฐาน API โปรโตคอล V1 สำหรับไคลเอนต์เก่าเท่านั้น",
|
||||
"enableDesc": "เมื่อปิด แอปนี้จะปฏิเสธคำขอโปรโตคอล EasyPay",
|
||||
"enableV2Desc": "แนะนำ; ใช้ V2 สำหรับการเชื่อมต่อใหม่",
|
||||
"enableV1Desc": "V1 มีความปลอดภัยต่ำ; เปิดใช้งานเฉพาะไคลเอนต์เก่า",
|
||||
"md5KeyDesc": "คีย์ MD5 สำหรับการเซ็นคำขอ V1",
|
||||
"useSystemKeyDesc": "ใช้คู่คีย์ของแพลตฟอร์มจึงไม่ต้องกรอกรหัสสาธารณะผู้ค้า",
|
||||
"publicKeyDesc": "รหัสสาธารณะ RSA ของผู้ค้า เมื่อใช้คู่คีย์ที่กำหนดเอง",
|
||||
"platformPublicKeyDesc": "รหัสสาธารณะของแพลตฟอร์มสำหรับการตรวจสอบลายเซ็นผู้เชื่อมต่อ"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,5 +47,38 @@
|
||||
"deleteDesc": "Điều này không thể hoàn tác được. Tiến hành thận trọng",
|
||||
"noActions": "Không có hành động nào",
|
||||
"openActionAria": "Mở cấu hình ứng dụng"
|
||||
},
|
||||
"easypay": {
|
||||
"v1Hint": "V1 có bảo mật yếu; chỉ bật cho khách hàng cũ",
|
||||
"useSystemKey": "Dùng cặp khóa hệ thống",
|
||||
"v2Url": "URL cơ sở API V2",
|
||||
"desc": "Cung cấp API giao thức EasyPay V1/V2",
|
||||
"title": "Cấu hình EasyPay",
|
||||
"notifyHint": "Bản này chưa gửi thông báo async; hãy polling query API",
|
||||
"platformPublicKey": "Khóa công khai RSA nền tảng",
|
||||
"enableV1": "Bật V1",
|
||||
"confirmSave": "Lưu cấu hình EasyPay?",
|
||||
"md5Key": "Khóa MD5 V1",
|
||||
"pid": "ID merchant EasyPay (pid)",
|
||||
"enableV2": "Bật V2",
|
||||
"md5KeyPh": "Nhập khóa MD5",
|
||||
"publicKey": "Khóa công khai RSA merchant",
|
||||
"v1Url": "URL cơ sở API V1",
|
||||
"enable": "Bật",
|
||||
"section": {
|
||||
"endpoint": "Cơ bản",
|
||||
"v1": "Giao thức V1",
|
||||
"v2": "Giao thức V2"
|
||||
},
|
||||
"pidDesc": "ID merchant EasyPay cấp theo ứng dụng, dùng làm pid bởi bên tích hợp",
|
||||
"v2UrlDesc": "URL cơ sở API giao thức V2 để chia sẻ với bên tích hợp",
|
||||
"v1UrlDesc": "URL cơ sở API giao thức V1, chỉ cho khách hàng cũ",
|
||||
"enableDesc": "Khi tắt, ứng dụng này từ chối yêu cầu giao thức EasyPay",
|
||||
"enableV2Desc": "Đề nghị; dùng V2 cho tích hợp mới",
|
||||
"enableV1Desc": "V1 có bảo mật yếu; chỉ bật cho khách hàng cũ",
|
||||
"md5KeyDesc": "Khóa MD5 dùng để ký yêu cầu V1",
|
||||
"useSystemKeyDesc": "Dùng cặp khóa nền tảng nên không cần khóa công khai merchant",
|
||||
"publicKeyDesc": "Khóa công khai RSA merchant khi dùng cặp khóa tùy chỉnh",
|
||||
"platformPublicKeyDesc": "Khóa công khai nền tảng để bên tích hợp xác minh chữ ký"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,5 +47,38 @@
|
||||
"deleteDesc": "删除后不可恢复,请谨慎操作",
|
||||
"noActions": "暂无可用操作",
|
||||
"openActionAria": "进入应用配置"
|
||||
},
|
||||
"easypay": {
|
||||
"v1Hint": "V1 安全性较弱,仅兼容旧系统时开启",
|
||||
"useSystemKey": "使用系统密钥",
|
||||
"v2Url": "V2 对接地址",
|
||||
"desc": "对外兼容易支付 V1/V2 协议接入",
|
||||
"title": "易支付配置",
|
||||
"notifyHint": "本期暂不发送异步通知,请使用 query 轮询订单状态",
|
||||
"platformPublicKey": "平台 RSA 公钥",
|
||||
"enableV1": "开启 V1",
|
||||
"confirmSave": "确定保存易支付配置吗?",
|
||||
"md5Key": "V1 MD5 密钥",
|
||||
"pid": "易支付商户号(pid)",
|
||||
"enableV2": "开启 V2",
|
||||
"md5KeyPh": "请输入 MD5 密钥",
|
||||
"publicKey": "商户 RSA 公钥",
|
||||
"v1Url": "V1 对接地址",
|
||||
"enable": "启用",
|
||||
"section": {
|
||||
"endpoint": "基础对接",
|
||||
"v1": "V1 协议配置",
|
||||
"v2": "V2 协议配置"
|
||||
},
|
||||
"pidDesc": "系统按应用分配的易支付商户号,对接方作为 pid 使用",
|
||||
"v2UrlDesc": "V2 协议 API 根地址,复制给对接方",
|
||||
"v1UrlDesc": "V1 协议 API 根地址,仅兼容旧系统时使用",
|
||||
"enableDesc": "关闭后该应用将拒绝易支付协议请求",
|
||||
"enableV2Desc": "推荐开启;新对接请走 V2",
|
||||
"enableV1Desc": "V1 安全性较弱,仅兼容旧系统时开启",
|
||||
"md5KeyDesc": "V1 签名用 MD5 密钥",
|
||||
"useSystemKeyDesc": "使用平台密钥对时无需填写商户公钥",
|
||||
"publicKeyDesc": "商户侧 RSA 公钥(自签时填写)",
|
||||
"platformPublicKeyDesc": "平台公钥,对接方验签使用"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -47,5 +47,38 @@
|
||||
"deleteDesc": "刪除後不可恢復,請謹慎操作",
|
||||
"noActions": "暫無可用操作",
|
||||
"openActionAria": "進入應用配置"
|
||||
},
|
||||
"easypay": {
|
||||
"v1Hint": "V1 安全性較弱,僅兼容舊系統時開啟",
|
||||
"useSystemKey": "使用系統密鑰",
|
||||
"v2Url": "V2 對接地址",
|
||||
"desc": "對外兼容易支付 V1/V2 協議接入",
|
||||
"title": "易支付配置",
|
||||
"notifyHint": "本期暫不發送異步通知,請使用 query 輪詢訂單狀態",
|
||||
"platformPublicKey": "平台 RSA 公鑰",
|
||||
"enableV1": "開啟 V1",
|
||||
"confirmSave": "確定保存易支付配置嗎?",
|
||||
"md5Key": "V1 MD5 密鑰",
|
||||
"pid": "易支付商戶號(pid)",
|
||||
"enableV2": "開啟 V2",
|
||||
"md5KeyPh": "請輸入 MD5 密鑰",
|
||||
"publicKey": "商戶 RSA 公鑰",
|
||||
"v1Url": "V1 對接地址",
|
||||
"enable": "啟用",
|
||||
"section": {
|
||||
"endpoint": "基礎對接",
|
||||
"v1": "V1 協議配置",
|
||||
"v2": "V2 協議配置"
|
||||
},
|
||||
"pidDesc": "系統按應用分配的易支付商戶號,對接方作為 pid 使用",
|
||||
"v2UrlDesc": "V2 協議 API 根位址,複製給對接方",
|
||||
"v1UrlDesc": "V1 協議 API 根位址,僅兼容舊系統時使用",
|
||||
"enableDesc": "關閉後該應用將拒絕易支付協議請求",
|
||||
"enableV2Desc": "建議開啟;新對接請走 V2",
|
||||
"enableV1Desc": "V1 安全性較弱,僅兼容舊系統時開啟",
|
||||
"md5KeyDesc": "V1 簽名用 MD5 密鑰",
|
||||
"useSystemKeyDesc": "使用平台密鑰對時無需填寫商戶公鑰",
|
||||
"publicKeyDesc": "商戶側 RSA 公鑰(自簽時填寫)",
|
||||
"platformPublicKeyDesc": "平台公鑰,對接方驗簽使用"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -47,5 +47,38 @@
|
||||
"deleteDesc": "刪除後不可恢復,請謹慎操作",
|
||||
"noActions": "暫無可用操作",
|
||||
"openActionAria": "進入應用配置"
|
||||
},
|
||||
"easypay": {
|
||||
"v1Hint": "V1 安全性較弱,僅兼容舊系統時開啟",
|
||||
"useSystemKey": "使用系統密鑰",
|
||||
"v2Url": "V2 對接地址",
|
||||
"desc": "對外兼容易支付 V1/V2 協議接入",
|
||||
"title": "易支付配置",
|
||||
"notifyHint": "本期暫不發送異步通知,請使用 query 輪詢訂單狀態",
|
||||
"platformPublicKey": "平台 RSA 公鑰",
|
||||
"enableV1": "開啟 V1",
|
||||
"confirmSave": "確定保存易支付配置嗎?",
|
||||
"md5Key": "V1 MD5 密鑰",
|
||||
"pid": "易支付商戶號(pid)",
|
||||
"enableV2": "開啟 V2",
|
||||
"md5KeyPh": "請輸入 MD5 密鑰",
|
||||
"publicKey": "商戶 RSA 公鑰",
|
||||
"v1Url": "V1 對接地址",
|
||||
"enable": "啟用",
|
||||
"section": {
|
||||
"endpoint": "基礎對接",
|
||||
"v1": "V1 協議配置",
|
||||
"v2": "V2 協議配置"
|
||||
},
|
||||
"pidDesc": "系統按應用分配的易支付商戶號,對接方作為 pid 使用",
|
||||
"v2UrlDesc": "V2 協議 API 根位址,複製給對接方",
|
||||
"v1UrlDesc": "V1 協議 API 根位址,僅兼容舊系統時使用",
|
||||
"enableDesc": "關閉後該應用將拒絕易支付協議請求",
|
||||
"enableV2Desc": "建議開啟;新對接請走 V2",
|
||||
"enableV1Desc": "V1 安全性較弱,僅兼容舊系統時開啟",
|
||||
"md5KeyDesc": "V1 簽名用 MD5 密鑰",
|
||||
"useSystemKeyDesc": "使用平台密鑰對時無需填寫商戶公鑰",
|
||||
"publicKeyDesc": "商戶側 RSA 公鑰(自簽時填寫)",
|
||||
"platformPublicKeyDesc": "平台公鑰,對接方驗簽使用"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -78,5 +78,6 @@
|
||||
"menu.payment.merchant.appWorkbench": "App Configuration",
|
||||
"menu.payment.lakala.config": "Lakala ISV Configuration",
|
||||
"menu.payment.merchant.group.channelApp": "Channel App Config",
|
||||
"menu.payment.config.group.channelIsv": "Channel ISV Config"
|
||||
"menu.payment.config.group.channelIsv": "Channel ISV Config",
|
||||
"menu.payment.merchant.easypay": "EasyPay Config"
|
||||
}
|
||||
|
||||
@@ -78,5 +78,6 @@
|
||||
"menu.payment.merchant.appWorkbench": "Konfigurasi Aplikasi",
|
||||
"menu.payment.lakala.config": "Konfigurasi ISV Lakala",
|
||||
"menu.payment.merchant.group.channelApp": "Konfigurasi Aplikasi Saluran",
|
||||
"menu.payment.config.group.channelIsv": "Konfigurasi ISV Saluran"
|
||||
"menu.payment.config.group.channelIsv": "Konfigurasi ISV Saluran",
|
||||
"menu.payment.merchant.easypay": "EasyPay Config"
|
||||
}
|
||||
|
||||
@@ -78,5 +78,6 @@
|
||||
"menu.payment.merchant.appWorkbench": "アプリの構成",
|
||||
"menu.payment.lakala.config": "Lakala ISV 構成",
|
||||
"menu.payment.merchant.group.channelApp": "チャネルアプリ構成",
|
||||
"menu.payment.config.group.channelIsv": "チャネル ISV 構成"
|
||||
"menu.payment.config.group.channelIsv": "チャネル ISV 構成",
|
||||
"menu.payment.merchant.easypay": "EasyPay設定"
|
||||
}
|
||||
|
||||
@@ -78,5 +78,6 @@
|
||||
"menu.payment.merchant.appWorkbench": "앱 구성",
|
||||
"menu.payment.lakala.config": "Lakala ISV 구성",
|
||||
"menu.payment.merchant.group.channelApp": "채널 앱 구성",
|
||||
"menu.payment.config.group.channelIsv": "채널 ISV 구성"
|
||||
"menu.payment.config.group.channelIsv": "채널 ISV 구성",
|
||||
"menu.payment.merchant.easypay": "EasyPay 설정"
|
||||
}
|
||||
|
||||
@@ -78,5 +78,6 @@
|
||||
"menu.payment.merchant.appWorkbench": "Konfigurasi Apl",
|
||||
"menu.payment.lakala.config": "Konfigurasi ISV Lakala",
|
||||
"menu.payment.merchant.group.channelApp": "Konfigurasi Apl Saluran",
|
||||
"menu.payment.config.group.channelIsv": "Saluran ISV Config"
|
||||
"menu.payment.config.group.channelIsv": "Saluran ISV Config",
|
||||
"menu.payment.merchant.easypay": "EasyPay Config"
|
||||
}
|
||||
|
||||
@@ -78,5 +78,6 @@
|
||||
"menu.payment.merchant.appWorkbench": "การกำหนดค่าแอป",
|
||||
"menu.payment.lakala.config": "การกำหนดค่า Lakala ISV",
|
||||
"menu.payment.merchant.group.channelApp": "การกำหนดค่าแอปช่อง",
|
||||
"menu.payment.config.group.channelIsv": "การกำหนดค่า ISV ของช่องสัญญาณ"
|
||||
"menu.payment.config.group.channelIsv": "การกำหนดค่า ISV ของช่องสัญญาณ",
|
||||
"menu.payment.merchant.easypay": "EasyPay Config"
|
||||
}
|
||||
|
||||
@@ -78,5 +78,6 @@
|
||||
"menu.payment.merchant.appWorkbench": "Cấu hình ứng dụng",
|
||||
"menu.payment.lakala.config": "Cấu hình Lakala ISV",
|
||||
"menu.payment.merchant.group.channelApp": "Cấu hình ứng dụng kênh",
|
||||
"menu.payment.config.group.channelIsv": "Cấu hình ISV kênh"
|
||||
"menu.payment.config.group.channelIsv": "Cấu hình ISV kênh",
|
||||
"menu.payment.merchant.easypay": "EasyPay Config"
|
||||
}
|
||||
|
||||
@@ -78,5 +78,6 @@
|
||||
"menu.payment.merchant.appWorkbench": "应用配置",
|
||||
"menu.payment.lakala.config": "拉卡拉服务商配置",
|
||||
"menu.payment.merchant.group.channelApp": "通道应用配置",
|
||||
"menu.payment.config.group.channelIsv": "通道服务商配置"
|
||||
"menu.payment.config.group.channelIsv": "通道服务商配置",
|
||||
"menu.payment.merchant.easypay": "易支付配置"
|
||||
}
|
||||
|
||||
@@ -78,5 +78,6 @@
|
||||
"menu.payment.merchant.appWorkbench": "應用配置",
|
||||
"menu.payment.lakala.config": "拉卡拉服務商配置",
|
||||
"menu.payment.merchant.group.channelApp": "通道應用配置",
|
||||
"menu.payment.config.group.channelIsv": "通道服務商配置"
|
||||
"menu.payment.config.group.channelIsv": "通道服務商配置",
|
||||
"menu.payment.merchant.easypay": "易支付配置"
|
||||
}
|
||||
|
||||
@@ -78,5 +78,6 @@
|
||||
"menu.payment.merchant.appWorkbench": "應用配置",
|
||||
"menu.payment.lakala.config": "拉卡拉服務商配置",
|
||||
"menu.payment.merchant.group.channelApp": "通道應用配置",
|
||||
"menu.payment.config.group.channelIsv": "通道服務商配置"
|
||||
"menu.payment.config.group.channelIsv": "通道服務商配置",
|
||||
"menu.payment.merchant.easypay": "易支付配置"
|
||||
}
|
||||
|
||||
@@ -106,6 +106,17 @@
|
||||
action: 'notify',
|
||||
});
|
||||
}
|
||||
// 易支付协议配置:subpage /payment/merchant/easypay?mchNo&appId
|
||||
if (hasPermission(PermCodes.Merchant.EasyPay.VIEW)) {
|
||||
paymentCards.push({
|
||||
key: 'easypay',
|
||||
// 卡片标题与菜单 i18n_key 一致
|
||||
title: $t('menu.payment.merchant.easypay'),
|
||||
icon: 'ant-design:api-outlined',
|
||||
description: $t('payment.merchant.app.easypay.desc'),
|
||||
route: '/payment/merchant/easypay',
|
||||
});
|
||||
}
|
||||
if (paymentCards.length > 0) {
|
||||
groups.push({
|
||||
group: $t('payment.merchant.app.app.groupConfig'),
|
||||
|
||||
@@ -0,0 +1,754 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed, onMounted, reactive, ref, watch } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
|
||||
import { $t } from '@vben/locales';
|
||||
|
||||
import { IconifyIcon } from '@vben-core/icons';
|
||||
|
||||
import { useClipboard } from '@vueuse/core';
|
||||
|
||||
import {
|
||||
EasyPayCredentialApi,
|
||||
type EasyPayCredentialParam,
|
||||
type EasyPayCredentialResult,
|
||||
} from '#/api/payment/merchant/easypay-credential.api';
|
||||
import { MchAppInfoApi, type MchAppInfoResult } from '#/api/payment/merchant/mch-app-info.api';
|
||||
import RouteQueryMissingState from '#/components/route/RouteQueryMissingState.vue';
|
||||
import { PermCodes } from '#/constants/perm-codes';
|
||||
import { useFormEdit } from '#/hooks/useFormEdit';
|
||||
import { useMessage } from '#/hooks/useMessage';
|
||||
import { usePermission } from '#/hooks/usePermission';
|
||||
import { normalizeRouteQueryValue, useRequiredRouteQuery } from '#/hooks/useRequiredRouteQuery';
|
||||
|
||||
defineOptions({ name: 'EasyPayConfig' });
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const { copy } = useClipboard();
|
||||
const { confirm, message } = useMessage();
|
||||
const { hasPermission } = usePermission();
|
||||
const { diffForm } = useFormEdit();
|
||||
|
||||
const routeContext = useRequiredRouteQuery({
|
||||
keys: ['mchNo', 'appId'],
|
||||
messageKey: computed(() =>
|
||||
normalizeRouteQueryValue(route.query.mchNo)
|
||||
? 'payment.common.route.missingAppContext'
|
||||
: 'payment.common.route.missingMchNo',
|
||||
),
|
||||
fallbackPath: computed(() => {
|
||||
const no = normalizeRouteQueryValue(route.query.mchNo);
|
||||
const app = normalizeRouteQueryValue(route.query.appId);
|
||||
if (no && app) {
|
||||
return { path: '/payment/merchant/app/manage', query: { mchNo: no, appId: app } };
|
||||
}
|
||||
return no ? { path: '/payment/merchant/app', query: { mchNo: no } } : '/payment/merchant';
|
||||
}),
|
||||
});
|
||||
|
||||
const mchNo = computed(() => routeContext.query.value.mchNo);
|
||||
const appId = computed(() => routeContext.query.value.appId);
|
||||
const canManage = computed(() => hasPermission(PermCodes.Merchant.EasyPay.MANAGE));
|
||||
|
||||
const loading = ref(false);
|
||||
const saving = ref(false);
|
||||
// 编辑模式
|
||||
const isEditing = ref(false);
|
||||
const appInfo = ref<MchAppInfoResult>({});
|
||||
const form = reactive<EasyPayCredentialResult>({
|
||||
enable: false,
|
||||
enableV1: false,
|
||||
enableV2: true,
|
||||
useSystemKey: true,
|
||||
});
|
||||
// 原始脱敏数据,用于 diffForm 比对
|
||||
const originalForm = ref<EasyPayCredentialResult>({});
|
||||
|
||||
/**
|
||||
* 平台公钥规范化:去掉 PEM 头尾与空白,得到可直接复制的纯 Base64
|
||||
*/
|
||||
function normalizePublicKey(pem?: string) {
|
||||
if (!pem) {
|
||||
return pem;
|
||||
}
|
||||
return pem
|
||||
.replace(/-----BEGIN PUBLIC KEY-----/g, '')
|
||||
.replace(/-----END PUBLIC KEY-----/g, '')
|
||||
.replace(/\s+/g, '');
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置表单默认值后合并服务端数据
|
||||
*/
|
||||
function applyCredential(data?: EasyPayCredentialResult | null) {
|
||||
Object.assign(
|
||||
form,
|
||||
{
|
||||
id: undefined,
|
||||
pid: undefined,
|
||||
appId: appId.value,
|
||||
enable: false,
|
||||
enableV1: false,
|
||||
enableV2: true,
|
||||
useSystemKey: true,
|
||||
md5Key: undefined,
|
||||
publicKey: undefined,
|
||||
platformPublicKey: undefined,
|
||||
easyPayV1ApiUrl: undefined,
|
||||
easyPayV2ApiUrl: undefined,
|
||||
},
|
||||
data || {},
|
||||
);
|
||||
// 展示/复制用纯 Base64(与商业版、后端 Result 一致;前端再兜底一次)
|
||||
form.platformPublicKey = normalizePublicKey(form.platformPublicKey);
|
||||
originalForm.value = { ...form };
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载应用信息(页头展示名)
|
||||
*/
|
||||
async function loadAppInfo() {
|
||||
if (!mchNo.value || !appId.value) {
|
||||
return;
|
||||
}
|
||||
const { data } = await MchAppInfoApi.page({
|
||||
mchNo: mchNo.value,
|
||||
current: 1,
|
||||
size: 200,
|
||||
});
|
||||
const app = data?.records?.find((a) => a.appId === appId.value);
|
||||
appInfo.value = app || {};
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载易支付凭证
|
||||
*/
|
||||
async function loadCredential() {
|
||||
if (!appId.value) {
|
||||
return;
|
||||
}
|
||||
loading.value = true;
|
||||
try {
|
||||
const { data } = await EasyPayCredentialApi.getByAppId(appId.value);
|
||||
applyCredential(data);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回应用工作台
|
||||
*/
|
||||
function handleBack() {
|
||||
router.push({
|
||||
path: '/payment/merchant/app/manage',
|
||||
query: { mchNo: mchNo.value, appId: appId.value },
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 进入编辑模式
|
||||
*/
|
||||
function handleEdit() {
|
||||
isEditing.value = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消编辑
|
||||
*/
|
||||
function handleCancel() {
|
||||
confirm({
|
||||
title: $t('common.confirm'),
|
||||
content: $t('common.confirmCancelContent'),
|
||||
okText: $t('common.okText'),
|
||||
cancelText: $t('common.cancelText'),
|
||||
onOk: async () => {
|
||||
await loadCredential();
|
||||
isEditing.value = false;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 复制文本
|
||||
*/
|
||||
function handleCopy(text?: string | number | null) {
|
||||
if (text === undefined || text === null || text === '') {
|
||||
return;
|
||||
}
|
||||
copy(String(text));
|
||||
message.success($t('common.operationSuccess'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存配置
|
||||
*/
|
||||
function handleSave() {
|
||||
if (!form.appId || !canManage.value) {
|
||||
return;
|
||||
}
|
||||
confirm({
|
||||
title: $t('common.confirm'),
|
||||
content: $t('payment.merchant.app.easypay.confirmSave'),
|
||||
okText: $t('common.okText'),
|
||||
cancelText: $t('common.cancelText'),
|
||||
onOk: async () => {
|
||||
saving.value = true;
|
||||
try {
|
||||
// 敏感字段未修改时不提交
|
||||
const sensitiveData = diffForm(originalForm, form, 'md5Key', 'publicKey');
|
||||
const submitData: EasyPayCredentialParam = {
|
||||
id: form.id,
|
||||
appId: form.appId!,
|
||||
enable: form.enable,
|
||||
enableV1: form.enableV1,
|
||||
enableV2: form.enableV2,
|
||||
useSystemKey: form.useSystemKey,
|
||||
...sensitiveData,
|
||||
};
|
||||
await EasyPayCredentialApi.update(submitData);
|
||||
message.success($t('common.operationSuccess'));
|
||||
isEditing.value = false;
|
||||
await loadCredential();
|
||||
} finally {
|
||||
saving.value = false;
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async function bootstrap() {
|
||||
if (!routeContext.isValid.value) {
|
||||
return;
|
||||
}
|
||||
isEditing.value = false;
|
||||
await loadAppInfo();
|
||||
await loadCredential();
|
||||
}
|
||||
|
||||
watch(
|
||||
() => [routeContext.query.value.mchNo, routeContext.query.value.appId],
|
||||
() => {
|
||||
bootstrap();
|
||||
},
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
bootstrap();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<RouteQueryMissingState
|
||||
v-if="!routeContext.isValid"
|
||||
:description="$t('payment.common.route.missingAppContext')"
|
||||
:back-text="$t('payment.merchant.app.app.backToAppList')"
|
||||
@back="routeContext.goFallback"
|
||||
/>
|
||||
<div v-else class="m-4">
|
||||
<a-card variant="borderless" class="rounded-xl shadow-sm">
|
||||
<template #title>
|
||||
<div class="flex items-center gap-2">
|
||||
<a-button
|
||||
type="text"
|
||||
class="flex items-center justify-center rounded-full hover:bg-accent"
|
||||
@click="handleBack"
|
||||
>
|
||||
<template #icon>
|
||||
<IconifyIcon icon="ant-design:arrow-left-outlined" class="text-lg" />
|
||||
</template>
|
||||
</a-button>
|
||||
<!-- 页头与菜单 i18n_key 一致 -->
|
||||
<span class="text-lg font-bold text-foreground">
|
||||
{{ $t('menu.payment.merchant.easypay') }}
|
||||
</span>
|
||||
<span v-if="appInfo.appName" class="text-sm text-muted-foreground">
|
||||
({{ appInfo.appName }})
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
<template #extra>
|
||||
<template v-if="canManage">
|
||||
<template v-if="!isEditing">
|
||||
<a-button type="primary" @click="handleEdit">{{ $t('common.edit') }}</a-button>
|
||||
</template>
|
||||
<template v-else>
|
||||
<a-space>
|
||||
<a-button @click="handleCancel">{{ $t('common.cancelText') }}</a-button>
|
||||
<a-button type="primary" :loading="saving" @click="handleSave">
|
||||
{{ $t('common.save') }}
|
||||
</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<a-spin :spinning="loading">
|
||||
<div class="easypay-form-container max-w-6xl px-4 py-2">
|
||||
<!-- 信息提示 -->
|
||||
<div class="info-banner">
|
||||
<IconifyIcon icon="ant-design:info-circle-filled" />
|
||||
<span>{{ $t('payment.merchant.app.easypay.desc') }}</span>
|
||||
</div>
|
||||
<div class="info-banner warning">
|
||||
<IconifyIcon icon="ant-design:warning-filled" />
|
||||
<span>{{ $t('payment.merchant.app.easypay.notifyHint') }}</span>
|
||||
</div>
|
||||
|
||||
<a-form layout="vertical" class="module-form">
|
||||
<!-- ① 基础对接 -->
|
||||
<div class="config-section">
|
||||
<div class="config-section__title">
|
||||
{{ $t('payment.merchant.app.easypay.section.endpoint') }}
|
||||
</div>
|
||||
<div class="config-grid">
|
||||
<!-- 总开关:半列 -->
|
||||
<div class="config-item config-item--block">
|
||||
<div class="config-item__main">
|
||||
<div class="config-item__label">
|
||||
{{ $t('payment.merchant.app.easypay.enable') }}
|
||||
</div>
|
||||
<div class="config-item__desc">
|
||||
{{ $t('payment.merchant.app.easypay.enableDesc') }}
|
||||
</div>
|
||||
</div>
|
||||
<a-radio-group
|
||||
v-model:value="form.enable"
|
||||
button-style="solid"
|
||||
:disabled="!isEditing"
|
||||
>
|
||||
<a-radio-button :value="true">{{ $t('common.enable') }}</a-radio-button>
|
||||
<a-radio-button :value="false">{{ $t('common.disable') }}</a-radio-button>
|
||||
</a-radio-group>
|
||||
</div>
|
||||
|
||||
<!-- pid:半列 -->
|
||||
<div class="config-item config-item--block">
|
||||
<div class="config-item__main">
|
||||
<div class="config-item__label">
|
||||
{{ $t('payment.merchant.app.easypay.pid') }}
|
||||
</div>
|
||||
<div class="config-item__desc">
|
||||
{{ $t('payment.merchant.app.easypay.pidDesc') }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="readonly-field">
|
||||
<a-input :value="form.pid" disabled class="font-mono text-sm" />
|
||||
<a-button type="link" size="small" @click="handleCopy(form.pid)">
|
||||
<template #icon>
|
||||
<IconifyIcon icon="ant-design:copy-outlined" />
|
||||
</template>
|
||||
{{ $t('common.copy') }}
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- V2 URL:整行 -->
|
||||
<div class="config-item config-item--block config-item--full">
|
||||
<div class="config-item__main">
|
||||
<div class="config-item__label">
|
||||
{{ $t('payment.merchant.app.easypay.v2Url') }}
|
||||
</div>
|
||||
<div class="config-item__desc">
|
||||
{{ $t('payment.merchant.app.easypay.v2UrlDesc') }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="readonly-field">
|
||||
<a-input :value="form.easyPayV2ApiUrl" disabled class="font-mono text-sm" />
|
||||
<a-button type="link" size="small" @click="handleCopy(form.easyPayV2ApiUrl)">
|
||||
<template #icon>
|
||||
<IconifyIcon icon="ant-design:copy-outlined" />
|
||||
</template>
|
||||
{{ $t('common.copy') }}
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- V1 URL:整行 -->
|
||||
<div class="config-item config-item--block config-item--full">
|
||||
<div class="config-item__main">
|
||||
<div class="config-item__label">
|
||||
{{ $t('payment.merchant.app.easypay.v1Url') }}
|
||||
</div>
|
||||
<div class="config-item__desc">
|
||||
{{ $t('payment.merchant.app.easypay.v1UrlDesc') }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="readonly-field">
|
||||
<a-input :value="form.easyPayV1ApiUrl" disabled class="font-mono text-sm" />
|
||||
<a-button type="link" size="small" @click="handleCopy(form.easyPayV1ApiUrl)">
|
||||
<template #icon>
|
||||
<IconifyIcon icon="ant-design:copy-outlined" />
|
||||
</template>
|
||||
{{ $t('common.copy') }}
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ② V1 协议配置 -->
|
||||
<div class="config-section">
|
||||
<div class="config-section__title">
|
||||
{{ $t('payment.merchant.app.easypay.section.v1') }}
|
||||
</div>
|
||||
<div class="config-grid">
|
||||
<!-- 开启 V1:半列 -->
|
||||
<div class="config-item config-item--block">
|
||||
<div class="config-item__main">
|
||||
<div class="config-item__label">
|
||||
{{ $t('payment.merchant.app.easypay.enableV1') }}
|
||||
</div>
|
||||
<div class="config-item__desc">
|
||||
{{ $t('payment.merchant.app.easypay.enableV1Desc') }}
|
||||
</div>
|
||||
</div>
|
||||
<a-radio-group
|
||||
v-model:value="form.enableV1"
|
||||
button-style="solid"
|
||||
:disabled="!isEditing"
|
||||
>
|
||||
<a-radio-button :value="true">{{ $t('common.enable') }}</a-radio-button>
|
||||
<a-radio-button :value="false">{{ $t('common.disable') }}</a-radio-button>
|
||||
</a-radio-group>
|
||||
</div>
|
||||
|
||||
<!-- MD5:半列,仅 V1 开启时显示 -->
|
||||
<div v-if="form.enableV1" class="config-item config-item--block">
|
||||
<div class="config-item__main">
|
||||
<div class="config-item__label">
|
||||
{{ $t('payment.merchant.app.easypay.md5Key') }}
|
||||
</div>
|
||||
<div class="config-item__desc">
|
||||
{{ $t('payment.merchant.app.easypay.md5KeyDesc') }}
|
||||
</div>
|
||||
</div>
|
||||
<a-input
|
||||
v-model:value="form.md5Key"
|
||||
:disabled="!isEditing"
|
||||
:placeholder="$t('payment.merchant.app.easypay.md5KeyPh')"
|
||||
allow-clear
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ③ V2 协议配置 -->
|
||||
<div class="config-section">
|
||||
<div class="config-section__title">
|
||||
{{ $t('payment.merchant.app.easypay.section.v2') }}
|
||||
</div>
|
||||
<div class="config-grid">
|
||||
<!-- 开启 V2:半列 -->
|
||||
<div class="config-item config-item--block">
|
||||
<div class="config-item__main">
|
||||
<div class="config-item__label">
|
||||
{{ $t('payment.merchant.app.easypay.enableV2') }}
|
||||
</div>
|
||||
<div class="config-item__desc">
|
||||
{{ $t('payment.merchant.app.easypay.enableV2Desc') }}
|
||||
</div>
|
||||
</div>
|
||||
<a-radio-group
|
||||
v-model:value="form.enableV2"
|
||||
button-style="solid"
|
||||
:disabled="!isEditing"
|
||||
>
|
||||
<a-radio-button :value="true">{{ $t('common.enable') }}</a-radio-button>
|
||||
<a-radio-button :value="false">{{ $t('common.disable') }}</a-radio-button>
|
||||
</a-radio-group>
|
||||
</div>
|
||||
|
||||
<!-- 使用系统密钥:半列,仅 V2 开启时显示 -->
|
||||
<div v-if="form.enableV2" class="config-item config-item--block">
|
||||
<div class="config-item__main">
|
||||
<div class="config-item__label">
|
||||
{{ $t('payment.merchant.app.easypay.useSystemKey') }}
|
||||
</div>
|
||||
<div class="config-item__desc">
|
||||
{{ $t('payment.merchant.app.easypay.useSystemKeyDesc') }}
|
||||
</div>
|
||||
</div>
|
||||
<a-radio-group
|
||||
v-model:value="form.useSystemKey"
|
||||
button-style="solid"
|
||||
:disabled="!isEditing"
|
||||
>
|
||||
<a-radio-button :value="true">{{ $t('common.yes') }}</a-radio-button>
|
||||
<a-radio-button :value="false">{{ $t('common.no') }}</a-radio-button>
|
||||
</a-radio-group>
|
||||
</div>
|
||||
|
||||
<!-- 平台公钥:整行,仅 V2 开启 -->
|
||||
<div
|
||||
v-if="form.enableV2"
|
||||
class="config-item config-item--block config-item--full"
|
||||
>
|
||||
<div class="config-item__main">
|
||||
<div class="config-item__label">
|
||||
{{ $t('payment.merchant.app.easypay.platformPublicKey') }}
|
||||
</div>
|
||||
<div class="config-item__desc">
|
||||
{{ $t('payment.merchant.app.easypay.platformPublicKeyDesc') }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="textarea-wrapper">
|
||||
<a-textarea
|
||||
:value="form.platformPublicKey"
|
||||
:rows="4"
|
||||
readonly
|
||||
class="readonly-textarea font-mono text-sm"
|
||||
/>
|
||||
<div class="textarea-actions">
|
||||
<a-button
|
||||
type="link"
|
||||
size="small"
|
||||
@click="handleCopy(form.platformPublicKey)"
|
||||
>
|
||||
<template #icon>
|
||||
<IconifyIcon icon="ant-design:copy-outlined" />
|
||||
</template>
|
||||
{{ $t('common.copy') }}
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 商户公钥:整行,V2 开启且不使用系统密钥 -->
|
||||
<div
|
||||
v-if="form.enableV2 && !form.useSystemKey"
|
||||
class="config-item config-item--block config-item--full"
|
||||
>
|
||||
<div class="config-item__main">
|
||||
<div class="config-item__label">
|
||||
{{ $t('payment.merchant.app.easypay.publicKey') }}
|
||||
</div>
|
||||
<div class="config-item__desc">
|
||||
{{ $t('payment.merchant.app.easypay.publicKeyDesc') }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="textarea-wrapper">
|
||||
<a-textarea
|
||||
v-model:value="form.publicKey"
|
||||
:rows="4"
|
||||
:disabled="!isEditing"
|
||||
allow-clear
|
||||
class="font-mono text-sm"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-form>
|
||||
</div>
|
||||
</a-spin>
|
||||
</a-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="less">
|
||||
.easypay-form-container {
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.module-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 28px;
|
||||
}
|
||||
|
||||
.info-banner {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
align-items: flex-start;
|
||||
padding: 12px 16px;
|
||||
margin-bottom: 12px;
|
||||
font-size: 13px;
|
||||
line-height: 1.6;
|
||||
color: hsl(var(--foreground));
|
||||
background-color: hsl(var(--primary) / 5%);
|
||||
border: 1px solid hsl(var(--primary) / 20%);
|
||||
border-radius: 8px;
|
||||
|
||||
:deep(.iconify) {
|
||||
flex-shrink: 0;
|
||||
margin-top: 2px;
|
||||
font-size: 18px;
|
||||
color: hsl(var(--primary));
|
||||
}
|
||||
|
||||
&.warning {
|
||||
margin-bottom: 24px;
|
||||
background-color: hsl(var(--warning) / 5%);
|
||||
border-color: hsl(var(--warning) / 20%);
|
||||
|
||||
:deep(.iconify) {
|
||||
color: hsl(var(--warning));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.config-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.config-section__title {
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: hsl(var(--foreground));
|
||||
}
|
||||
|
||||
/* 两列栅格:默认半列,整行用 --full */
|
||||
.config-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.config-item {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 100%;
|
||||
padding: 14px 16px;
|
||||
background: hsl(var(--card));
|
||||
border: 1px solid hsl(var(--border));
|
||||
border-radius: 12px;
|
||||
transition:
|
||||
border-color 0.2s ease,
|
||||
box-shadow 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
border-color: hsl(var(--primary) / 30%);
|
||||
box-shadow: 0 1px 2px rgb(15 23 42 / 4%);
|
||||
}
|
||||
}
|
||||
|
||||
.config-item--block {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.config-item--full {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
|
||||
.config-item__main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.config-item__label {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: hsl(var(--foreground));
|
||||
}
|
||||
|
||||
.config-item__desc {
|
||||
margin-top: 4px;
|
||||
font-size: 12px;
|
||||
line-height: 1.6;
|
||||
color: hsl(var(--muted-foreground));
|
||||
}
|
||||
|
||||
.readonly-field {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
margin-top: 10px;
|
||||
|
||||
:deep(.ant-input) {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.config-item--block > .ant-radio-group {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.config-item--block > .ant-input,
|
||||
.config-item--block > .ant-input-affix-wrapper {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.textarea-wrapper {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
margin-top: 10px;
|
||||
overflow: hidden;
|
||||
background: hsl(var(--card));
|
||||
border: 1px solid hsl(var(--border));
|
||||
border-radius: 6px;
|
||||
transition: all 0.3s;
|
||||
|
||||
&:hover {
|
||||
border-color: hsl(var(--primary) / 50%);
|
||||
}
|
||||
|
||||
&:focus-within {
|
||||
border-color: hsl(var(--primary));
|
||||
box-shadow: 0 0 0 2px hsl(var(--primary) / 20%);
|
||||
}
|
||||
|
||||
:deep(.ant-input) {
|
||||
padding: 12px;
|
||||
font-size: 13px;
|
||||
color: hsl(var(--foreground));
|
||||
resize: none;
|
||||
background: transparent;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
|
||||
&:focus {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
&::placeholder {
|
||||
color: hsl(var(--muted-foreground));
|
||||
}
|
||||
}
|
||||
|
||||
.readonly-textarea {
|
||||
cursor: default;
|
||||
background-color: hsl(var(--muted) / 50%);
|
||||
|
||||
:deep(.ant-input) {
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
|
||||
.textarea-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding: 6px 12px;
|
||||
background: hsl(var(--muted) / 30%);
|
||||
border-top: 1px solid hsl(var(--border));
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.ant-input-disabled),
|
||||
:deep(.ant-input[disabled]) {
|
||||
color: hsl(var(--muted-foreground));
|
||||
cursor: not-allowed;
|
||||
background-color: hsl(var(--muted) / 30%);
|
||||
|
||||
&:hover {
|
||||
border-color: hsl(var(--border));
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.config-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.config-item--full {
|
||||
grid-column: auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user