mirror of
https://gitee.com/bootx/dax-pay-ui
synced 2026-08-12 07:25:39 +08:00
feat(easypay): 商户RSA密钥对在线生成与公私钥纯Base64规范化
- 两端 EasyPayConfig 商户公钥区新增"生成密钥对"按钮与私钥弹窗 - 生成/保存时去除PEM头尾与换行,公私钥均以纯Base64存储(易支付要求) - easypay 节点新增5个i18n key,两端10语种同步
This commit is contained in:
@@ -82,6 +82,11 @@
|
||||
"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"
|
||||
"platformPublicKeyDesc": "Platform public key for integrator signature verification",
|
||||
"genRsaKeyPair": "Generate Key Pair",
|
||||
"confirmGenPublicKey": "The public key already exists. Regenerating will overwrite it. Continue?",
|
||||
"genKeyPairTitle": "RSA Key Pair",
|
||||
"privateKey": "Private Key",
|
||||
"privateKeyWarning": "The private key is shown only once. Keep it safe — it cannot be recovered if lost."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,6 +82,11 @@
|
||||
"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"
|
||||
"platformPublicKeyDesc": "Kunci publik platform untuk verifikasi tanda tangan integrator",
|
||||
"genRsaKeyPair": "Buat Pasangan Kunci",
|
||||
"confirmGenPublicKey": "Kunci publik sudah ada. Membuat ulang akan menimpa kunci yang ada. Lanjutkan?",
|
||||
"genKeyPairTitle": "Pasangan Kunci RSA",
|
||||
"privateKey": "Kunci Privat",
|
||||
"privateKeyWarning": "Kunci privat hanya ditampilkan sekali di sini. Simpan dengan aman — tidak dapat dipulihkan jika hilang."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,6 +82,11 @@
|
||||
"md5KeyDesc": "V1 署名用の MD5 鍵",
|
||||
"useSystemKeyDesc": "プラットフォーム鍵ペアを使う場合、加盟店公開鍵の入力は不要です",
|
||||
"publicKeyDesc": "独自鍵ペア利用時の加盟店 RSA 公開鍵",
|
||||
"platformPublicKeyDesc": "連携側の署名検証用プラットフォーム公開鍵"
|
||||
"platformPublicKeyDesc": "連携側の署名検証用プラットフォーム公開鍵",
|
||||
"genRsaKeyPair": "鍵ペアを生成",
|
||||
"confirmGenPublicKey": "公開鍵が既に存在します。再生成すると元の公開鍵が上書きされます。再生成しますか?",
|
||||
"genKeyPairTitle": "RSA鍵ペア",
|
||||
"privateKey": "秘密鍵",
|
||||
"privateKeyWarning": "秘密鍵はここに一度だけ表示されます。確実に保管してください。紛失すると復元できません。"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,6 +82,11 @@
|
||||
"md5KeyDesc": "V1 서명용 MD5 키",
|
||||
"useSystemKeyDesc": "플랫폼 키 쌍을 사용하면 가맹점 공개키를 입력할 필요가 없습니다",
|
||||
"publicKeyDesc": "자체 키 쌍 사용 시 가맹점 RSA 공개키",
|
||||
"platformPublicKeyDesc": "연동측 서명 검증용 플랫폼 공개키"
|
||||
"platformPublicKeyDesc": "연동측 서명 검증용 플랫폼 공개키",
|
||||
"genRsaKeyPair": "키 쌍 생성",
|
||||
"confirmGenPublicKey": "공개 키가 이미 존재합니다. 다시 생성하면 기존 공개 키를 덮어씁니다. 다시 생성하시겠습니까?",
|
||||
"genKeyPairTitle": "RSA 키 쌍",
|
||||
"privateKey": "개인 키",
|
||||
"privateKeyWarning": "개인 키는 여기에 한 번만 표시됩니다. 안전하게 보관하세요. 분실 시 복구할 수 없습니다."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,6 +82,11 @@
|
||||
"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"
|
||||
"platformPublicKeyDesc": "Kunci awam platform untuk pengesahan tandatangan pengintegrasi",
|
||||
"genRsaKeyPair": "Jana Pasangan Kunci",
|
||||
"confirmGenPublicKey": "Kunci awam telah wujud. Menjana semula akan menulis ganti kunci sedia ada. Teruskan?",
|
||||
"genKeyPairTitle": "Pasangan Kunci RSA",
|
||||
"privateKey": "Kunci Peribadi",
|
||||
"privateKeyWarning": "Kunci peribadi hanya dipaparkan sekali di sini. Simpan dengan selamat — tidak boleh dipulihkan jika hilang."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,6 +82,11 @@
|
||||
"md5KeyDesc": "คีย์ MD5 สำหรับการเซ็นคำขอ V1",
|
||||
"useSystemKeyDesc": "ใช้คู่คีย์ของแพลตฟอร์มจึงไม่ต้องกรอกรหัสสาธารณะผู้ค้า",
|
||||
"publicKeyDesc": "รหัสสาธารณะ RSA ของผู้ค้า เมื่อใช้คู่คีย์ที่กำหนดเอง",
|
||||
"platformPublicKeyDesc": "รหัสสาธารณะของแพลตฟอร์มสำหรับการตรวจสอบลายเซ็นผู้เชื่อมต่อ"
|
||||
"platformPublicKeyDesc": "รหัสสาธารณะของแพลตฟอร์มสำหรับการตรวจสอบลายเซ็นผู้เชื่อมต่อ",
|
||||
"genRsaKeyPair": "สร้างคู่คีย์",
|
||||
"confirmGenPublicKey": "มีคีย์สาธารณะอยู่แล้ว การสร้างใหม่จะเขียนทับคีย์เดิม ต้องการสร้างใหม่หรือไม่?",
|
||||
"genKeyPairTitle": "คู่คีย์ RSA",
|
||||
"privateKey": "คีย์ส่วนตัว",
|
||||
"privateKeyWarning": "คีย์ส่วนตัวจะแสดงที่นี่เพียงครั้งเดียว โปรดเก็บรักษาให้ดี หากสูญหายจะกู้คืนไม่ได้"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,6 +82,11 @@
|
||||
"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ý"
|
||||
"platformPublicKeyDesc": "Khóa công khai nền tảng để bên tích hợp xác minh chữ ký",
|
||||
"genRsaKeyPair": "Tạo cặp khóa",
|
||||
"confirmGenPublicKey": "Khóa công khai đã tồn tại. Tạo lại sẽ ghi đè khóa hiện có. Tiếp tục?",
|
||||
"genKeyPairTitle": "Cặp khóa RSA",
|
||||
"privateKey": "Khóa riêng",
|
||||
"privateKeyWarning": "Khóa riêng chỉ hiển thị một lần tại đây. Vui lòng lưu giữ an toàn — không thể khôi phục nếu mất."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,6 +82,11 @@
|
||||
"md5KeyDesc": "V1 签名用 MD5 密钥",
|
||||
"useSystemKeyDesc": "使用平台密钥对时无需填写商户公钥",
|
||||
"publicKeyDesc": "商户侧 RSA 公钥(自签时填写)",
|
||||
"platformPublicKeyDesc": "平台公钥,对接方验签使用"
|
||||
"platformPublicKeyDesc": "平台公钥,对接方验签使用",
|
||||
"genRsaKeyPair": "生成密钥对",
|
||||
"confirmGenPublicKey": "公钥已存在,重新生成将覆盖原有公钥,确定要重新生成吗?",
|
||||
"genKeyPairTitle": "RSA密钥对",
|
||||
"privateKey": "私钥",
|
||||
"privateKeyWarning": "私钥仅在此处展示一次,请务必妥善保管,丢失无法找回"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,6 +82,11 @@
|
||||
"md5KeyDesc": "V1 簽名用 MD5 密鑰",
|
||||
"useSystemKeyDesc": "使用平台密鑰對時無需填寫商戶公鑰",
|
||||
"publicKeyDesc": "商戶側 RSA 公鑰(自簽時填寫)",
|
||||
"platformPublicKeyDesc": "平台公鑰,對接方驗簽使用"
|
||||
"platformPublicKeyDesc": "平台公鑰,對接方驗簽使用",
|
||||
"genRsaKeyPair": "產生金鑰對",
|
||||
"confirmGenPublicKey": "公鑰已存在,重新產生將覆蓋原有公鑰,確定要重新產生嗎?",
|
||||
"genKeyPairTitle": "RSA金鑰對",
|
||||
"privateKey": "私鑰",
|
||||
"privateKeyWarning": "私鑰僅在此處顯示一次,請務必妥善保管,遺失無法找回"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,6 +82,11 @@
|
||||
"md5KeyDesc": "V1 簽名用 MD5 密鑰",
|
||||
"useSystemKeyDesc": "使用平台密鑰對時無需填寫商戶公鑰",
|
||||
"publicKeyDesc": "商戶側 RSA 公鑰(自簽時填寫)",
|
||||
"platformPublicKeyDesc": "平台公鑰,對接方驗簽使用"
|
||||
"platformPublicKeyDesc": "平台公鑰,對接方驗簽使用",
|
||||
"genRsaKeyPair": "產生金鑰對",
|
||||
"confirmGenPublicKey": "公鑰已存在,重新產生將覆蓋原有公鑰,確定要重新產生嗎?",
|
||||
"genKeyPairTitle": "RSA金鑰對",
|
||||
"privateKey": "私鑰",
|
||||
"privateKeyWarning": "私鑰僅在此處顯示一次,請務必妥善保管,遺失無法找回"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
type EasyPayCredentialParam,
|
||||
type EasyPayCredentialResult,
|
||||
} from '#/api/payment/merchant/easypay-credential.api';
|
||||
import { KeyGenApi } from '#/api/payment/merchant/key-gen.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';
|
||||
@@ -64,18 +65,36 @@
|
||||
});
|
||||
// 原始脱敏数据,用于 diffForm 比对
|
||||
const originalForm = ref<EasyPayCredentialResult>({});
|
||||
// RSA 密钥对弹窗
|
||||
const keyPairVisible = ref(false);
|
||||
const privateKeyContent = ref('');
|
||||
|
||||
/**
|
||||
* 平台公钥规范化:去掉 PEM 头尾与空白,得到可直接复制的纯 Base64
|
||||
* 公钥规范化:去掉 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, '');
|
||||
.replaceAll('-----BEGIN PUBLIC KEY-----', '')
|
||||
.replaceAll('-----END PUBLIC KEY-----', '')
|
||||
.replaceAll(/\s+/g, '');
|
||||
}
|
||||
|
||||
/**
|
||||
* 私钥规范化:去掉 PEM 头尾与空白,得到可直接复制的纯 Base64(易支付要求)
|
||||
*/
|
||||
function normalizePrivateKey(pem?: string) {
|
||||
if (!pem) {
|
||||
return pem;
|
||||
}
|
||||
return pem
|
||||
.replaceAll('-----BEGIN PRIVATE KEY-----', '')
|
||||
.replaceAll('-----END PRIVATE KEY-----', '')
|
||||
.replaceAll('-----BEGIN RSA PRIVATE KEY-----', '')
|
||||
.replaceAll('-----END RSA PRIVATE KEY-----', '')
|
||||
.replaceAll(/\s+/g, '');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -173,7 +192,7 @@
|
||||
/**
|
||||
* 复制文本
|
||||
*/
|
||||
function handleCopy(text?: string | number | null) {
|
||||
function handleCopy(text?: null | number | string) {
|
||||
if (text === undefined || text === null || text === '') {
|
||||
return;
|
||||
}
|
||||
@@ -181,6 +200,39 @@
|
||||
message.success($t('common.operationSuccess'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成 RSA 密钥对(公钥已存在时二次确认)
|
||||
*/
|
||||
async function handleGenRsaKeyPair() {
|
||||
if (form.publicKey) {
|
||||
confirm({
|
||||
title: $t('common.confirm'),
|
||||
content: $t('payment.merchant.app.easypay.confirmGenPublicKey'),
|
||||
okText: $t('common.okText'),
|
||||
cancelText: $t('common.cancelText'),
|
||||
onOk: async () => {
|
||||
await doGenRsaKeyPair();
|
||||
},
|
||||
});
|
||||
} else {
|
||||
await doGenRsaKeyPair();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行生成 RSA 密钥对:公钥去 PEM 头尾后填入表单,私钥弹窗展示
|
||||
*/
|
||||
async function doGenRsaKeyPair() {
|
||||
const { data } = await KeyGenApi.genRsaKeyPair();
|
||||
if (data) {
|
||||
// 公钥去注释与换行(纯 Base64 存储),前端处理
|
||||
form.publicKey = normalizePublicKey(data.publicKey);
|
||||
// 私钥同样去 PEM 头尾与换行(易支付要求纯 Base64),弹窗展示
|
||||
privateKeyContent.value = normalizePrivateKey(data.privateKey) ?? '';
|
||||
keyPairVisible.value = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存配置
|
||||
*/
|
||||
@@ -196,6 +248,10 @@
|
||||
onOk: async () => {
|
||||
saving.value = true;
|
||||
try {
|
||||
// 商户公钥去 PEM 头尾与换行(纯 Base64 存储),保存时兜底处理
|
||||
if (form.publicKey) {
|
||||
form.publicKey = normalizePublicKey(form.publicKey);
|
||||
}
|
||||
// 敏感字段未修改时不提交
|
||||
const sensitiveData = diffForm(originalForm, form, 'md5Key', 'publicKey');
|
||||
const submitData: EasyPayCredentialParam = {
|
||||
@@ -263,9 +319,7 @@
|
||||
<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>
|
||||
<span v-if="appInfo.appName" class="text-sm text-muted-foreground"> ({{ appInfo.appName }}) </span>
|
||||
</div>
|
||||
</template>
|
||||
<template #extra>
|
||||
@@ -313,11 +367,7 @@
|
||||
{{ $t('payment.merchant.app.easypay.enableDesc') }}
|
||||
</div>
|
||||
</div>
|
||||
<a-radio-group
|
||||
v-model:value="form.enable"
|
||||
button-style="solid"
|
||||
:disabled="!isEditing"
|
||||
>
|
||||
<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>
|
||||
@@ -404,11 +454,7 @@
|
||||
{{ $t('payment.merchant.app.easypay.enableV1Desc') }}
|
||||
</div>
|
||||
</div>
|
||||
<a-radio-group
|
||||
v-model:value="form.enableV1"
|
||||
button-style="solid"
|
||||
:disabled="!isEditing"
|
||||
>
|
||||
<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>
|
||||
@@ -450,11 +496,7 @@
|
||||
{{ $t('payment.merchant.app.easypay.enableV2Desc') }}
|
||||
</div>
|
||||
</div>
|
||||
<a-radio-group
|
||||
v-model:value="form.enableV2"
|
||||
button-style="solid"
|
||||
:disabled="!isEditing"
|
||||
>
|
||||
<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>
|
||||
@@ -470,21 +512,14 @@
|
||||
{{ $t('payment.merchant.app.easypay.useSystemKeyDesc') }}
|
||||
</div>
|
||||
</div>
|
||||
<a-radio-group
|
||||
v-model:value="form.useSystemKey"
|
||||
button-style="solid"
|
||||
:disabled="!isEditing"
|
||||
>
|
||||
<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 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') }}
|
||||
@@ -501,11 +536,7 @@
|
||||
class="readonly-textarea font-mono text-sm"
|
||||
/>
|
||||
<div class="textarea-actions">
|
||||
<a-button
|
||||
type="link"
|
||||
size="small"
|
||||
@click="handleCopy(form.platformPublicKey)"
|
||||
>
|
||||
<a-button type="link" size="small" @click="handleCopy(form.platformPublicKey)">
|
||||
<template #icon>
|
||||
<IconifyIcon icon="ant-design:copy-outlined" />
|
||||
</template>
|
||||
@@ -536,6 +567,14 @@
|
||||
allow-clear
|
||||
class="font-mono text-sm"
|
||||
/>
|
||||
<div class="textarea-actions">
|
||||
<a-button type="link" size="small" :disabled="!isEditing" @click="handleGenRsaKeyPair">
|
||||
<template #icon>
|
||||
<IconifyIcon icon="ant-design:key-outlined" />
|
||||
</template>
|
||||
{{ $t('payment.merchant.app.easypay.genRsaKeyPair') }}
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -544,6 +583,38 @@
|
||||
</div>
|
||||
</a-spin>
|
||||
</a-card>
|
||||
|
||||
<!-- RSA 密钥对弹窗 -->
|
||||
<a-modal
|
||||
v-model:open="keyPairVisible"
|
||||
:title="$t('payment.merchant.app.easypay.genKeyPairTitle')"
|
||||
:footer="null"
|
||||
width="640px"
|
||||
:mask-closable="false"
|
||||
>
|
||||
<div class="space-y-4">
|
||||
<div class="info-banner warning">
|
||||
<IconifyIcon icon="ant-design:warning-filled" />
|
||||
<span>{{ $t('payment.merchant.app.easypay.privateKeyWarning') }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<div class="mb-2 flex items-center justify-between">
|
||||
<span class="font-medium text-foreground">
|
||||
{{ $t('payment.merchant.app.easypay.privateKey') }}
|
||||
</span>
|
||||
<a-button size="small" type="primary" ghost @click="handleCopy(privateKeyContent)">
|
||||
<template #icon>
|
||||
<IconifyIcon icon="ant-design:copy-outlined" />
|
||||
</template>
|
||||
{{ $t('common.copy') }}
|
||||
</a-button>
|
||||
</div>
|
||||
<div class="textarea-wrapper">
|
||||
<a-textarea :value="privateKeyContent" :rows="10" readonly class="readonly-textarea font-mono text-sm" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -86,6 +86,11 @@
|
||||
"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"
|
||||
"platformPublicKeyDesc": "Platform public key for integrator signature verification",
|
||||
"genRsaKeyPair": "Generate Key Pair",
|
||||
"confirmGenPublicKey": "The public key already exists. Regenerating will overwrite it. Continue?",
|
||||
"genKeyPairTitle": "RSA Key Pair",
|
||||
"privateKey": "Private Key",
|
||||
"privateKeyWarning": "The private key is shown only once. Keep it safe — it cannot be recovered if lost."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,6 +86,11 @@
|
||||
"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"
|
||||
"platformPublicKeyDesc": "Kunci publik platform untuk verifikasi tanda tangan integrator",
|
||||
"genRsaKeyPair": "Buat Pasangan Kunci",
|
||||
"confirmGenPublicKey": "Kunci publik sudah ada. Membuat ulang akan menimpa kunci yang ada. Lanjutkan?",
|
||||
"genKeyPairTitle": "Pasangan Kunci RSA",
|
||||
"privateKey": "Kunci Privat",
|
||||
"privateKeyWarning": "Kunci privat hanya ditampilkan sekali di sini. Simpan dengan aman — tidak dapat dipulihkan jika hilang."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,6 +86,11 @@
|
||||
"md5KeyDesc": "V1 署名用の MD5 鍵",
|
||||
"useSystemKeyDesc": "プラットフォーム鍵ペアを使う場合、加盟店公開鍵の入力は不要です",
|
||||
"publicKeyDesc": "独自鍵ペア利用時の加盟店 RSA 公開鍵",
|
||||
"platformPublicKeyDesc": "連携側の署名検証用プラットフォーム公開鍵"
|
||||
"platformPublicKeyDesc": "連携側の署名検証用プラットフォーム公開鍵",
|
||||
"genRsaKeyPair": "鍵ペアを生成",
|
||||
"confirmGenPublicKey": "公開鍵が既に存在します。再生成すると元の公開鍵が上書きされます。再生成しますか?",
|
||||
"genKeyPairTitle": "RSA鍵ペア",
|
||||
"privateKey": "秘密鍵",
|
||||
"privateKeyWarning": "秘密鍵はここに一度だけ表示されます。確実に保管してください。紛失すると復元できません。"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,6 +86,11 @@
|
||||
"md5KeyDesc": "V1 서명용 MD5 키",
|
||||
"useSystemKeyDesc": "플랫폼 키 쌍을 사용하면 가맹점 공개키를 입력할 필요가 없습니다",
|
||||
"publicKeyDesc": "자체 키 쌍 사용 시 가맹점 RSA 공개키",
|
||||
"platformPublicKeyDesc": "연동측 서명 검증용 플랫폼 공개키"
|
||||
"platformPublicKeyDesc": "연동측 서명 검증용 플랫폼 공개키",
|
||||
"genRsaKeyPair": "키 쌍 생성",
|
||||
"confirmGenPublicKey": "공개 키가 이미 존재합니다. 다시 생성하면 기존 공개 키를 덮어씁니다. 다시 생성하시겠습니까?",
|
||||
"genKeyPairTitle": "RSA 키 쌍",
|
||||
"privateKey": "개인 키",
|
||||
"privateKeyWarning": "개인 키는 여기에 한 번만 표시됩니다. 안전하게 보관하세요. 분실 시 복구할 수 없습니다."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,6 +86,11 @@
|
||||
"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"
|
||||
"platformPublicKeyDesc": "Kunci awam platform untuk pengesahan tandatangan pengintegrasi",
|
||||
"genRsaKeyPair": "Jana Pasangan Kunci",
|
||||
"confirmGenPublicKey": "Kunci awam telah wujud. Menjana semula akan menulis ganti kunci sedia ada. Teruskan?",
|
||||
"genKeyPairTitle": "Pasangan Kunci RSA",
|
||||
"privateKey": "Kunci Peribadi",
|
||||
"privateKeyWarning": "Kunci peribadi hanya dipaparkan sekali di sini. Simpan dengan selamat — tidak boleh dipulihkan jika hilang."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,6 +86,11 @@
|
||||
"md5KeyDesc": "คีย์ MD5 สำหรับการเซ็นคำขอ V1",
|
||||
"useSystemKeyDesc": "ใช้คู่คีย์ของแพลตฟอร์มจึงไม่ต้องกรอกรหัสสาธารณะผู้ค้า",
|
||||
"publicKeyDesc": "รหัสสาธารณะ RSA ของผู้ค้า เมื่อใช้คู่คีย์ที่กำหนดเอง",
|
||||
"platformPublicKeyDesc": "รหัสสาธารณะของแพลตฟอร์มสำหรับการตรวจสอบลายเซ็นผู้เชื่อมต่อ"
|
||||
"platformPublicKeyDesc": "รหัสสาธารณะของแพลตฟอร์มสำหรับการตรวจสอบลายเซ็นผู้เชื่อมต่อ",
|
||||
"genRsaKeyPair": "สร้างคู่คีย์",
|
||||
"confirmGenPublicKey": "มีคีย์สาธารณะอยู่แล้ว การสร้างใหม่จะเขียนทับคีย์เดิม ต้องการสร้างใหม่หรือไม่?",
|
||||
"genKeyPairTitle": "คู่คีย์ RSA",
|
||||
"privateKey": "คีย์ส่วนตัว",
|
||||
"privateKeyWarning": "คีย์ส่วนตัวจะแสดงที่นี่เพียงครั้งเดียว โปรดเก็บรักษาให้ดี หากสูญหายจะกู้คืนไม่ได้"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,6 +86,11 @@
|
||||
"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ý"
|
||||
"platformPublicKeyDesc": "Khóa công khai nền tảng để bên tích hợp xác minh chữ ký",
|
||||
"genRsaKeyPair": "Tạo cặp khóa",
|
||||
"confirmGenPublicKey": "Khóa công khai đã tồn tại. Tạo lại sẽ ghi đè khóa hiện có. Tiếp tục?",
|
||||
"genKeyPairTitle": "Cặp khóa RSA",
|
||||
"privateKey": "Khóa riêng",
|
||||
"privateKeyWarning": "Khóa riêng chỉ hiển thị một lần tại đây. Vui lòng lưu giữ an toàn — không thể khôi phục nếu mất."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,6 +86,11 @@
|
||||
"md5KeyDesc": "V1 签名用 MD5 密钥",
|
||||
"useSystemKeyDesc": "使用平台密钥对时无需填写商户公钥",
|
||||
"publicKeyDesc": "商户侧 RSA 公钥(自签时填写)",
|
||||
"platformPublicKeyDesc": "平台公钥,对接方验签使用"
|
||||
"platformPublicKeyDesc": "平台公钥,对接方验签使用",
|
||||
"genRsaKeyPair": "生成密钥对",
|
||||
"confirmGenPublicKey": "公钥已存在,重新生成将覆盖原有公钥,确定要重新生成吗?",
|
||||
"genKeyPairTitle": "RSA密钥对",
|
||||
"privateKey": "私钥",
|
||||
"privateKeyWarning": "私钥仅在此处展示一次,请务必妥善保管,丢失无法找回"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,6 +86,11 @@
|
||||
"md5KeyDesc": "V1 簽名用 MD5 密鑰",
|
||||
"useSystemKeyDesc": "使用平台密鑰對時無需填寫商戶公鑰",
|
||||
"publicKeyDesc": "商戶側 RSA 公鑰(自簽時填寫)",
|
||||
"platformPublicKeyDesc": "平台公鑰,對接方驗簽使用"
|
||||
"platformPublicKeyDesc": "平台公鑰,對接方驗簽使用",
|
||||
"genRsaKeyPair": "產生金鑰對",
|
||||
"confirmGenPublicKey": "公鑰已存在,重新產生將覆蓋原有公鑰,確定要重新產生嗎?",
|
||||
"genKeyPairTitle": "RSA金鑰對",
|
||||
"privateKey": "私鑰",
|
||||
"privateKeyWarning": "私鑰僅在此處顯示一次,請務必妥善保管,遺失無法找回"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,6 +86,11 @@
|
||||
"md5KeyDesc": "V1 簽名用 MD5 密鑰",
|
||||
"useSystemKeyDesc": "使用平台密鑰對時無需填寫商戶公鑰",
|
||||
"publicKeyDesc": "商戶側 RSA 公鑰(自簽時填寫)",
|
||||
"platformPublicKeyDesc": "平台公鑰,對接方驗簽使用"
|
||||
"platformPublicKeyDesc": "平台公鑰,對接方驗簽使用",
|
||||
"genRsaKeyPair": "產生金鑰對",
|
||||
"confirmGenPublicKey": "公鑰已存在,重新產生將覆蓋原有公鑰,確定要重新產生嗎?",
|
||||
"genKeyPairTitle": "RSA金鑰對",
|
||||
"privateKey": "私鑰",
|
||||
"privateKeyWarning": "私鑰僅在此處顯示一次,請務必妥善保管,遺失無法找回"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
type EasyPayCredentialParam,
|
||||
type EasyPayCredentialResult,
|
||||
} from '#/api/payment/merchant/easypay-credential.api';
|
||||
import { KeyGenApi } from '#/api/payment/merchant/key-gen.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';
|
||||
@@ -54,9 +55,12 @@
|
||||
});
|
||||
// 原始脱敏数据,用于 diffForm 比对
|
||||
const originalForm = ref<EasyPayCredentialResult>({});
|
||||
// RSA 密钥对弹窗
|
||||
const keyPairVisible = ref(false);
|
||||
const privateKeyContent = ref('');
|
||||
|
||||
/**
|
||||
* 平台公钥规范化:去掉 PEM 头尾与空白,得到可直接复制的纯 Base64
|
||||
* 公钥规范化:去掉 PEM 头尾与空白,得到可直接复制的纯 Base64
|
||||
*/
|
||||
function normalizePublicKey(pem?: string) {
|
||||
if (!pem) {
|
||||
@@ -68,6 +72,21 @@
|
||||
.replaceAll(/\s+/g, '');
|
||||
}
|
||||
|
||||
/**
|
||||
* 私钥规范化:去掉 PEM 头尾与空白,得到可直接复制的纯 Base64(易支付要求)
|
||||
*/
|
||||
function normalizePrivateKey(pem?: string) {
|
||||
if (!pem) {
|
||||
return pem;
|
||||
}
|
||||
return pem
|
||||
.replaceAll('-----BEGIN PRIVATE KEY-----', '')
|
||||
.replaceAll('-----END PRIVATE KEY-----', '')
|
||||
.replaceAll('-----BEGIN RSA PRIVATE KEY-----', '')
|
||||
.replaceAll('-----END RSA PRIVATE KEY-----', '')
|
||||
.replaceAll(/\s+/g, '');
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置表单默认值后合并服务端数据
|
||||
*/
|
||||
@@ -163,6 +182,39 @@
|
||||
message.success($t('common.operationSuccess'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成 RSA 密钥对(公钥已存在时二次确认)
|
||||
*/
|
||||
async function handleGenRsaKeyPair() {
|
||||
if (form.publicKey) {
|
||||
confirm({
|
||||
title: $t('common.confirm'),
|
||||
content: $t('payment.merchant.app.easypay.confirmGenPublicKey'),
|
||||
okText: $t('common.okText'),
|
||||
cancelText: $t('common.cancelText'),
|
||||
onOk: async () => {
|
||||
await doGenRsaKeyPair();
|
||||
},
|
||||
});
|
||||
} else {
|
||||
await doGenRsaKeyPair();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行生成 RSA 密钥对:公钥去 PEM 头尾后填入表单,私钥弹窗展示
|
||||
*/
|
||||
async function doGenRsaKeyPair() {
|
||||
const { data } = await KeyGenApi.genRsaKeyPair();
|
||||
if (data) {
|
||||
// 公钥去注释与换行(纯 Base64 存储),前端处理
|
||||
form.publicKey = normalizePublicKey(data.publicKey);
|
||||
// 私钥同样去 PEM 头尾与换行(易支付要求纯 Base64),弹窗展示
|
||||
privateKeyContent.value = normalizePrivateKey(data.privateKey) ?? '';
|
||||
keyPairVisible.value = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存配置
|
||||
*/
|
||||
@@ -178,6 +230,10 @@
|
||||
onOk: async () => {
|
||||
saving.value = true;
|
||||
try {
|
||||
// 商户公钥去 PEM 头尾与换行(纯 Base64 存储),保存时兜底处理
|
||||
if (form.publicKey) {
|
||||
form.publicKey = normalizePublicKey(form.publicKey);
|
||||
}
|
||||
// 敏感字段未修改时不提交
|
||||
const sensitiveData = diffForm(originalForm.value, form, 'md5Key', 'publicKey');
|
||||
const submitData: EasyPayCredentialParam = {
|
||||
@@ -481,6 +537,14 @@
|
||||
allow-clear
|
||||
class="font-mono text-sm"
|
||||
/>
|
||||
<div class="textarea-actions">
|
||||
<a-button type="link" size="small" :disabled="!isEditing" @click="handleGenRsaKeyPair">
|
||||
<template #icon>
|
||||
<IconifyIcon icon="ant-design:key-outlined" />
|
||||
</template>
|
||||
{{ $t('payment.merchant.app.easypay.genRsaKeyPair') }}
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -489,6 +553,38 @@
|
||||
</div>
|
||||
</a-spin>
|
||||
</a-card>
|
||||
|
||||
<!-- RSA 密钥对弹窗 -->
|
||||
<a-modal
|
||||
v-model:open="keyPairVisible"
|
||||
:title="$t('payment.merchant.app.easypay.genKeyPairTitle')"
|
||||
:footer="null"
|
||||
width="640px"
|
||||
:mask-closable="false"
|
||||
>
|
||||
<div class="space-y-4">
|
||||
<div class="info-banner warning">
|
||||
<IconifyIcon icon="ant-design:warning-filled" />
|
||||
<span>{{ $t('payment.merchant.app.easypay.privateKeyWarning') }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<div class="mb-2 flex items-center justify-between">
|
||||
<span class="font-medium text-foreground">
|
||||
{{ $t('payment.merchant.app.easypay.privateKey') }}
|
||||
</span>
|
||||
<a-button size="small" type="primary" ghost @click="handleCopy(privateKeyContent)">
|
||||
<template #icon>
|
||||
<IconifyIcon icon="ant-design:copy-outlined" />
|
||||
</template>
|
||||
{{ $t('common.copy') }}
|
||||
</a-button>
|
||||
</div>
|
||||
<div class="textarea-wrapper">
|
||||
<a-textarea :value="privateKeyContent" :rows="10" readonly class="readonly-textarea font-mono text-sm" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user