refactor(admin): 支付调试页移除请求预览并补齐高级参数

- 移除普通/网关调试页的"请求预览"卡(实时JSON+签名预览), 清理相关 script 状态
- 两页右列新增"高级参数"折叠卡(a-collapse 默认收起)
  - 普通页: returnUrl/attach/limitPay/extraParam
  - 网关页: returnUrl(从卡2迁入)/attach/extraParam
- extraParam 失焦 JSON 校验, limitPay 支持 tags 模式手输(预置 no_credit)
- PayParam 接口补 attach/extraParam/limitPay 字段
- 10 语种 i18n 同步: 删 preview 段 + 补 advanced/field/placeholder/rule
This commit is contained in:
DaxPay Dev
2026-07-25 14:35:42 +08:00
parent 10a0e89c45
commit 35bd4a6119
13 changed files with 363 additions and 368 deletions

View File

@@ -82,6 +82,12 @@ export interface PayParam {
notifyUrl?: string;
/** 同步跳转地址 */
returnUrl?: string;
/** 商户附加参数(回调原样返回) */
attach?: string;
/** 支付扩展参数(JSON, 通道长尾参数) */
extraParam?: string;
/** 限制支付类型(如 no_credit 禁信用卡) */
limitPay?: string[];
/** 过期时间(北京时间 yyyy-MM-dd HH:mm:ss) */
expiredTime?: string;
/** 客户端 IP(可选, 未传时由 unipay 从 HTTP 请求提取) */

View File

@@ -60,7 +60,7 @@
"mchApp": "Merchant Info",
"param": "Payment Params",
"order": "Order Info",
"preview": "Request Preview"
"advanced": "Advanced Params"
},
"mode": {
"label": "Pay Mode",
@@ -69,11 +69,6 @@
"routeDesc": "Resolve channel merchant and capability via the app's channel routing",
"directDesc": "Directly specify channel merchant + capability; skip channel routing"
},
"preview": {
"title": "Request JSON",
"signBtn": "Sign Preview",
"empty": "No data"
},
"field": {
"privateKey": "Merchant Private Key",
"mchNo": "Merchant",
@@ -85,6 +80,10 @@
"openId": "OpenID",
"authCode": "Payment code",
"notifyUrl": "Async Notify URL",
"returnUrl": "Sync Return URL",
"attach": "Attach",
"extraParam": "Extra Params",
"limitPay": "Limit Pay",
"expiredTime": "Expire Time",
"bizOrderNo": "Order No.",
"amount": "Amount (Fen)",
@@ -97,6 +96,10 @@
"openId": "Required for WeChat jsapi/mini",
"authCode": "Required for barcode pay",
"notifyUrl": "https://...",
"returnUrl": "https://...",
"attach": "Returned as-is in callback",
"extraParam": "JSON, channel-specific params",
"limitPay": "Select or input, e.g. no_credit",
"expiredTime": "Default 30 minutes if empty",
"title": "Test order",
"description": "Enter payment description"
@@ -145,7 +148,8 @@
"amount": "Please enter the amount",
"method": "Please select a payment method",
"channelMchNo": "Please select a channel merchant",
"capability": "Please select a capability"
"capability": "Please select a capability",
"extraParam": "Extra params must be valid JSON"
},
"msg": {
"signSuccess": "Sign generated successfully",
@@ -232,7 +236,7 @@
"mchApp": "Merchant",
"order": "Order",
"param": "Extra Parameters",
"preview": "Request Preview"
"advanced": "Advanced Params"
},
"gatewayType": {
"label": "Gateway Type",
@@ -241,11 +245,6 @@
"cashierDesc": "Generate a cashier landing page where users choose a payment method",
"aggregateDesc": "Generate an aggregate QR link, one code for multiple payment methods"
},
"preview": {
"title": "Request JSON",
"signBtn": "Generate Sign Preview",
"empty": "No data"
},
"field": {
"privateKey": "Merchant Private Key",
"mchNo": "Merchant",
@@ -256,12 +255,16 @@
"description": "Description",
"notifyUrl": "Async Notify URL",
"returnUrl": "Sync Return URL",
"attach": "Attach",
"extraParam": "Extra Params",
"expiredTime": "Expire Time",
"storeNo": "Store No."
},
"placeholder": {
"notifyUrl": "https://...",
"returnUrl": "https://...",
"attach": "Returned as-is in callback",
"extraParam": "JSON, channel-specific params",
"expiredTime": "Default 30 min",
"storeNo": "Offline store number",
"title": "Test order",
@@ -304,7 +307,8 @@
"bizOrderNo": "Please enter order no.",
"title": "Please enter payment title",
"amount": "Please enter amount",
"gatewayPayType": "Please select gateway type"
"gatewayPayType": "Please select gateway type",
"extraParam": "Extra params must be valid JSON"
},
"msg": {
"signSuccess": "Sign generated",

View File

@@ -60,7 +60,7 @@
"mchApp": "Info Pedagang",
"param": "Parameter Pembayaran",
"order": "Info Pemesanan",
"preview": "Minta Pratinjau"
"advanced": "Parameter Lanjutan"
},
"mode": {
"label": "Modus Bayar",
@@ -69,11 +69,6 @@
"routeDesc": "Selesaikan pedagang saluran dan kemampuan melalui perutean saluran aplikasi",
"directDesc": "Diputuskan oleh pedagang saluran + kemampuan; lewati perutean saluran"
},
"preview": {
"title": "Minta JSON",
"signBtn": "Pratinjau Tanda Tangan",
"empty": "Tidak ada data"
},
"field": {
"privateKey": "Kunci Pribadi Pedagang",
"mchNo": "Pedagang",
@@ -89,7 +84,11 @@
"bizOrderNo": "Nomor Pesanan.",
"amount": "Jumlah (Fen)",
"title": "Judul",
"description": "Keterangan"
"description": "Keterangan",
"returnUrl": "URL Return Sync",
"attach": "Lampiran Merchant",
"extraParam": "Parameter Ekstra",
"limitPay": "Batasan Pembayaran"
},
"placeholder": {
"method": "Pilih metode pembayaran",
@@ -99,7 +98,11 @@
"notifyUrl": "https://...",
"expiredTime": "Default 30 menit jika kosong",
"title": "Urutan tes",
"description": "Masukkan deskripsi pembayaran"
"description": "Masukkan deskripsi pembayaran",
"returnUrl": "https://...",
"attach": "Dikembalikan apa adanya di callback",
"extraParam": "JSON, parameter khusus channel",
"limitPay": "Pilih atau input, mis. no_credit"
},
"default": {
"titlePrefix": "Tes Bayar"
@@ -145,7 +148,8 @@
"amount": "Silakan masukkan jumlahnya",
"method": "Silakan pilih metode pembayaran",
"channelMchNo": "Silakan pilih pedagang saluran",
"capability": "Silakan pilih kemampuan"
"capability": "Silakan pilih kemampuan",
"extraParam": "Parameter ekstra harus JSON valid"
},
"msg": {
"signSuccess": "Tanda tangan berhasil dibuat",
@@ -232,7 +236,7 @@
"mchApp": "Info Merchant",
"order": "Info Pesanan",
"param": "Parameter Tambahan",
"preview": "Pratinjau Permintaan"
"advanced": "Parameter Lanjutan"
},
"gatewayType": {
"label": "Tipe Gateway",
@@ -241,11 +245,6 @@
"cashierDesc": "Buat halaman kasir, pengguna memilih metode pembayaran",
"aggregateDesc": "Buat tautan QR agregat, satu kode untuk banyak pembayaran"
},
"preview": {
"title": "JSON Permintaan",
"signBtn": "Buat Pratinjau Tanda Tangan",
"empty": "Tidak ada data"
},
"field": {
"privateKey": "Kunci Pribadi Merchant",
"mchNo": "Merchant",
@@ -257,7 +256,9 @@
"notifyUrl": "URL Notifikasi Async",
"returnUrl": "URL Return Sync",
"expiredTime": "Waktu Kedaluwarsa",
"storeNo": "No. Toko"
"storeNo": "No. Toko",
"attach": "Lampiran Merchant",
"extraParam": "Parameter Ekstra"
},
"placeholder": {
"notifyUrl": "https://...",
@@ -265,7 +266,9 @@
"expiredTime": "Default 30 mnt",
"storeNo": "Nomor toko offline",
"title": "Pesanan uji",
"description": "Masukkan deskripsi pembayaran"
"description": "Masukkan deskripsi pembayaran",
"attach": "Dikembalikan apa adanya di callback",
"extraParam": "JSON, parameter khusus channel"
},
"default": {
"titlePrefix": "Tes Gateway Pay"
@@ -304,7 +307,8 @@
"bizOrderNo": "Silakan masukkan no. pesanan",
"title": "Silakan masukkan judul pembayaran",
"amount": "Silakan masukkan jumlah",
"gatewayPayType": "Silakan pilih tipe gateway"
"gatewayPayType": "Silakan pilih tipe gateway",
"extraParam": "Parameter ekstra harus JSON valid"
},
"msg": {
"signSuccess": "Tanda tangan dibuat",

View File

@@ -60,7 +60,7 @@
"mchApp": "販売者情報",
"param": "支払いパラメータ",
"order": "注文情報",
"preview": "プレビューのリクエスト"
"advanced": "高度パラメータ"
},
"mode": {
"label": "ペイモード",
@@ -69,11 +69,6 @@
"routeDesc": "アプリのチャネルルーティングを介してチャネルマーチャントと機能を解決します",
"directDesc": "チャネル販売業者と能力によって決定されます。チャンネルルーティングをスキップする"
},
"preview": {
"title": "JSONをリクエストする",
"signBtn": "署名プレビュー",
"empty": "データなし"
},
"field": {
"privateKey": "販売者の秘密鍵",
"mchNo": "商人",
@@ -89,7 +84,11 @@
"bizOrderNo": "注文番号",
"amount": "量(フェン)",
"title": "タイトル",
"description": "説明"
"description": "説明",
"returnUrl": "同期リダイレクトURL",
"attach": "マーチャント添付パラメータ",
"extraParam": "拡張パラメータ",
"limitPay": "支払制限タイプ"
},
"placeholder": {
"method": "支払い方法を選択してください",
@@ -99,7 +98,11 @@
"notifyUrl": "https://...",
"expiredTime": "空の場合はデフォルトで 30 分",
"title": "テストの順序",
"description": "支払いの説明を入力してください"
"description": "支払いの説明を入力してください",
"returnUrl": "https://...",
"attach": "コールバック時にそのまま返却",
"extraParam": "JSON形式, チャネル固有パラメータ",
"limitPay": "選択または入力(例: no_credit"
},
"default": {
"titlePrefix": "テストペイ"
@@ -145,7 +148,8 @@
"amount": "金額を入力してください",
"method": "お支払い方法を選択してください",
"channelMchNo": "チャネル販売者を選択してください",
"capability": "機能を選択してください"
"capability": "機能を選択してください",
"extraParam": "拡張パラメータは有効なJSON形式である必要があります"
},
"msg": {
"signSuccess": "署名は正常に生成されました",
@@ -232,7 +236,7 @@
"mchApp": "マーチャント情報",
"order": "注文情報",
"param": "拡張パラメータ",
"preview": "リクエストプレビュー"
"advanced": "高度パラメータ"
},
"gatewayType": {
"label": "ゲートウェイ種別",
@@ -241,11 +245,6 @@
"cashierDesc": "レジランディングページを生成し、ユーザーが支払い方法を選択",
"aggregateDesc": "集合QRリンクを生成し、一つのコードで複数支払いを自動識別"
},
"preview": {
"title": "リクエストJSON",
"signBtn": "署名プレビュー生成",
"empty": "データなし"
},
"field": {
"privateKey": "マーチャント秘密鍵",
"mchNo": "マーチャント",
@@ -257,7 +256,9 @@
"notifyUrl": "非同期通知URL",
"returnUrl": "同期リダイレクトURL",
"expiredTime": "有効期限",
"storeNo": "店舗番号"
"storeNo": "店舗番号",
"attach": "マーチャント添付パラメータ",
"extraParam": "拡張パラメータ"
},
"placeholder": {
"notifyUrl": "https://...",
@@ -265,7 +266,9 @@
"expiredTime": "空欄で30分",
"storeNo": "オフライン店舗番号",
"title": "テスト注文",
"description": "支払説明を入力"
"description": "支払説明を入力",
"attach": "コールバック時にそのまま返却",
"extraParam": "JSON形式, チャネル固有パラメータ"
},
"default": {
"titlePrefix": "テストゲートウェイ決済"
@@ -304,7 +307,8 @@
"bizOrderNo": "注文番号を入力してください",
"title": "支払タイトルを入力してください",
"amount": "支払金額を入力してください",
"gatewayPayType": "ゲートウェイ種別を選択してください"
"gatewayPayType": "ゲートウェイ種別を選択してください",
"extraParam": "拡張パラメータは有効なJSON形式である必要があります"
},
"msg": {
"signSuccess": "署名を生成しました",

View File

@@ -60,7 +60,7 @@
"mchApp": "판매자 정보",
"param": "결제 매개변수",
"order": "주문 정보",
"preview": "미리보기 요청"
"advanced": "고급 매개변수"
},
"mode": {
"label": "지불 모드",
@@ -69,11 +69,6 @@
"routeDesc": "앱의 채널 라우팅을 통해 채널 판매자 및 기능을 해결합니다.",
"directDesc": "채널 판매자 + 역량에 따라 결정됩니다. 채널 라우팅 건너뛰기"
},
"preview": {
"title": "JSON 요청",
"signBtn": "서명 미리보기",
"empty": "데이터 없음"
},
"field": {
"privateKey": "판매자 개인 키",
"mchNo": "상인",
@@ -89,7 +84,11 @@
"bizOrderNo": "주문번호",
"amount": "금액(펜)",
"title": "제목",
"description": "설명"
"description": "설명",
"returnUrl": "동기 리다이렉트 URL",
"attach": "가맹점 추가 매개변수",
"extraParam": "확장 매개변수",
"limitPay": "결제 제한 유형"
},
"placeholder": {
"method": "결제 수단 선택",
@@ -99,7 +98,11 @@
"notifyUrl": "https://...",
"expiredTime": "비어 있는 경우 기본값은 30분입니다.",
"title": "테스트 순서",
"description": "결제 설명 입력"
"description": "결제 설명 입력",
"returnUrl": "https://...",
"attach": "콜백 시 그대로 반환",
"extraParam": "JSON, 채널 고유 매개변수",
"limitPay": "선택 또는 입력, 예: no_credit"
},
"default": {
"titlePrefix": "테스트 페이"
@@ -145,7 +148,8 @@
"amount": "금액을 입력해주세요.",
"method": "결제수단을 선택해주세요",
"channelMchNo": "채널 판매자를 선택하세요.",
"capability": "기능을 선택해 주세요"
"capability": "기능을 선택해 주세요",
"extraParam": "확장 매개변수는 유효한 JSON 형식이어야 합니다"
},
"msg": {
"signSuccess": "서명이 성공적으로 생성되었습니다.",
@@ -232,7 +236,7 @@
"mchApp": "가맹점 정보",
"order": "주문 정보",
"param": "확장 매개변수",
"preview": "요청 미리보기"
"advanced": "고급 매개변수"
},
"gatewayType": {
"label": "게이트웨이 유형",
@@ -241,11 +245,6 @@
"cashierDesc": "캐시어 랜딩 페이지 생성, 사용자가 결제 방법 선택",
"aggregateDesc": "통합 QR 링크 생성, 하나의 코드로 다중 결제 자동 식별"
},
"preview": {
"title": "요청 JSON",
"signBtn": "서명 미리보기 생성",
"empty": "데이터 없음"
},
"field": {
"privateKey": "가맹점 개인키",
"mchNo": "가맹점",
@@ -257,7 +256,9 @@
"notifyUrl": "비동기 알림 URL",
"returnUrl": "동기 리다이렉트 URL",
"expiredTime": "만료 시간",
"storeNo": "매장 번호"
"storeNo": "매장 번호",
"attach": "가맹점 추가 매개변수",
"extraParam": "확장 매개변수"
},
"placeholder": {
"notifyUrl": "https://...",
@@ -265,7 +266,9 @@
"expiredTime": "비워두면 30분",
"storeNo": "오프라인 매장 번호",
"title": "테스트 주문",
"description": "결제 설명 입력"
"description": "결제 설명 입력",
"attach": "콜백 시 그대로 반환",
"extraParam": "JSON, 채널 고유 매개변수"
},
"default": {
"titlePrefix": "테스트 게이트웨이 결제"
@@ -304,7 +307,8 @@
"bizOrderNo": "주문번호를 입력해 주세요",
"title": "결제 제목을 입력해 주세요",
"amount": "결제 금액을 입력해 주세요",
"gatewayPayType": "게이트웨이 유형을 선택해 주세요"
"gatewayPayType": "게이트웨이 유형을 선택해 주세요",
"extraParam": "확장 매개변수는 유효한 JSON 형식이어야 합니다"
},
"msg": {
"signSuccess": "서명이 생성되었습니다",

View File

@@ -60,7 +60,7 @@
"mchApp": "Maklumat Peniaga",
"param": "Param Pembayaran",
"order": "Maklumat Pesanan",
"preview": "Minta Pratonton"
"advanced": "Parameter Lanjutan"
},
"mode": {
"label": "Mod Bayar",
@@ -69,11 +69,6 @@
"routeDesc": "Selesaikan pedagang saluran dan keupayaan melalui penghalaan saluran apl",
"directDesc": "Diputuskan oleh pedagang saluran + keupayaan; langkau penghalaan saluran"
},
"preview": {
"title": "Minta JSON",
"signBtn": "Pratonton Tandatangan",
"empty": "Tiada data"
},
"field": {
"privateKey": "Kunci Peribadi Pedagang",
"mchNo": "saudagar",
@@ -89,7 +84,11 @@
"bizOrderNo": "Pesanan No.",
"amount": "Jumlah (Fen)",
"title": "Tajuk",
"description": "Penerangan"
"description": "Penerangan",
"returnUrl": "URL Pulang Sync",
"attach": "Lampiran Pedagang",
"extraParam": "Parameter Ekstra",
"limitPay": "Had Pembayaran"
},
"placeholder": {
"method": "Pilih kaedah pembayaran",
@@ -99,7 +98,11 @@
"notifyUrl": "https://...",
"expiredTime": "Lalai 30 minit jika kosong",
"title": "Perintah ujian",
"description": "Masukkan perihalan pembayaran"
"description": "Masukkan perihalan pembayaran",
"returnUrl": "https://...",
"attach": "Dipulangkan seperti asal",
"extraParam": "JSON, parameter khusus saluran",
"limitPay": "Pilih atau input, cth: no_credit"
},
"default": {
"titlePrefix": "Uji Bayar"
@@ -145,7 +148,8 @@
"amount": "Sila masukkan jumlah",
"method": "Sila pilih kaedah pembayaran",
"channelMchNo": "Sila pilih pedagang saluran",
"capability": "Sila pilih keupayaan"
"capability": "Sila pilih keupayaan",
"extraParam": "Parameter ekstra mesti JSON sah"
},
"msg": {
"signSuccess": "Tanda berjaya dijana",
@@ -232,7 +236,7 @@
"mchApp": "Maklumat Merchant",
"order": "Maklumat Pesanan",
"param": "Parameter Tambahan",
"preview": "Pratonton Permintaan"
"advanced": "Parameter Lanjutan"
},
"gatewayType": {
"label": "Jenis Gateway",
@@ -241,11 +245,6 @@
"cashierDesc": "Jana halaman pendarat juruwang, pengguna memilih kaedah pembayaran",
"aggregateDesc": "Jana pautan QR agregat, satu kod untuk pelbagai pembayaran"
},
"preview": {
"title": "JSON Permintaan",
"signBtn": "Jana Pratonton Tandatangan",
"empty": "Tiada data"
},
"field": {
"privateKey": "Kunci Peribadi Merchant",
"mchNo": "Merchant",
@@ -257,7 +256,9 @@
"notifyUrl": "URL Pemberitahuan Async",
"returnUrl": "URL Pulang Sync",
"expiredTime": "Masa Tamat",
"storeNo": "No. Kedai"
"storeNo": "No. Kedai",
"attach": "Lampiran Pedagang",
"extraParam": "Parameter Ekstra"
},
"placeholder": {
"notifyUrl": "https://...",
@@ -265,7 +266,9 @@
"expiredTime": "Lalai 30 min",
"storeNo": "Nombor kedai luar talian",
"title": "Pesanan ujian",
"description": "Masukkan penerangan pembayaran"
"description": "Masukkan penerangan pembayaran",
"attach": "Dipulangkan seperti asal",
"extraParam": "JSON, parameter khusus saluran"
},
"default": {
"titlePrefix": "Uji Gateway Pay"
@@ -304,7 +307,8 @@
"bizOrderNo": "Sila masukkan no. pesanan",
"title": "Sila masukkan tajuk pembayaran",
"amount": "Sila masukkan jumlah",
"gatewayPayType": "Sila pilih jenis gateway"
"gatewayPayType": "Sila pilih jenis gateway",
"extraParam": "Parameter ekstra mesti JSON sah"
},
"msg": {
"signSuccess": "Tandatangan dijana",

View File

@@ -60,7 +60,7 @@
"mchApp": "ข้อมูลผู้ขาย",
"param": "พารามิเตอร์การชำระเงิน",
"order": "ข้อมูลการสั่งซื้อ",
"preview": "ขอดูตัวอย่าง"
"advanced": "พารามิเตอร์ขั้นสูง"
},
"mode": {
"label": "โหมดการชำระเงิน",
@@ -69,11 +69,6 @@
"routeDesc": "แก้ไขผู้ค้าช่องทางและความสามารถผ่านการกำหนดเส้นทางช่องทางของแอป",
"directDesc": "ตัดสินใจโดยช่องทางการค้า + ความสามารถ; ข้ามการกำหนดเส้นทางช่อง"
},
"preview": {
"title": "ขอ JSON",
"signBtn": "ลงชื่อแสดงตัวอย่าง",
"empty": "ไม่มีข้อมูล"
},
"field": {
"privateKey": "รหัสส่วนตัวของผู้ขาย",
"mchNo": "พ่อค้า",
@@ -89,7 +84,11 @@
"bizOrderNo": "หมายเลขคำสั่งซื้อ",
"amount": "จำนวนเงิน (เฟิน)",
"title": "ชื่อ",
"description": "คำอธิบาย"
"description": "คำอธิบาย",
"returnUrl": "URL กลับพร้อมกัน",
"attach": "พารามิเตอร์แนบท้าย",
"extraParam": "พารามิเตอร์เพิ่มเติม",
"limitPay": "จำกัดประเภทการชำระ"
},
"placeholder": {
"method": "เลือกวิธีการชำระเงิน",
@@ -99,7 +98,11 @@
"notifyUrl": "https://...",
"expiredTime": "ค่าเริ่มต้น 30 นาทีหากว่างเปล่า",
"title": "คำสั่งทดสอบ",
"description": "ป้อนคำอธิบายการชำระเงิน"
"description": "ป้อนคำอธิบายการชำระเงิน",
"returnUrl": "https://...",
"attach": "ส่งกลับตามเดิมเมื่อเรียกกลับ",
"extraParam": "JSON, พารามิเตอร์เฉพาะช่องทาง",
"limitPay": "เลือกหรือกรอก เช่น no_credit"
},
"default": {
"titlePrefix": "ทดสอบการจ่าย"
@@ -145,7 +148,8 @@
"amount": "กรุณากรอกจำนวนเงิน",
"method": "กรุณาเลือกวิธีการชำระเงิน",
"channelMchNo": "กรุณาเลือกช่องทางร้านค้า",
"capability": "โปรดเลือกความสามารถ"
"capability": "โปรดเลือกความสามารถ",
"extraParam": "พารามิเตอร์เพิ่มเติมต้องเป็น JSON ที่ถูกต้อง"
},
"msg": {
"signSuccess": "สร้างเครื่องหมายสำเร็จแล้ว",
@@ -232,7 +236,7 @@
"mchApp": "ข้อมูลร้านค้า",
"order": "ข้อมูลคำสั่ง",
"param": "พารามิเตอร์เพิ่มเติม",
"preview": "ดูตัวอย่างคำขอ"
"advanced": "พารามิเตอร์ขั้นสูง"
},
"gatewayType": {
"label": "ประเภทเกตเวย์",
@@ -241,11 +245,6 @@
"cashierDesc": "สร้างหน้าเคาเตอร์ ผู้ใช้เลือกวิธีการชำระเงิน",
"aggregateDesc": "สร้างลิงก์ QR รวม หนึ่งรหัสสำหรับหลายวิธีการชำระเงิน"
},
"preview": {
"title": "JSON คำขอ",
"signBtn": "สร้างตัวอย่างลายเซ็น",
"empty": "ไม่มีข้อมูล"
},
"field": {
"privateKey": "คีย์ส่วนตัวร้านค้า",
"mchNo": "ร้านค้า",
@@ -257,7 +256,9 @@
"notifyUrl": "URL แจ้งเตือน async",
"returnUrl": "URL กลับ sync",
"expiredTime": "เวลาหมดอายุ",
"storeNo": "เลขที่ร้าน"
"storeNo": "เลขที่ร้าน",
"attach": "พารามิเตอร์แนบท้าย",
"extraParam": "พารามิเตอร์เพิ่มเติม"
},
"placeholder": {
"notifyUrl": "https://...",
@@ -265,7 +266,9 @@
"expiredTime": "ค่าเริ่มต้น 30 นาที",
"storeNo": "เลขที่ร้านออฟไลน์",
"title": "คำสั่งทดสอบ",
"description": "ป้อนคำอธิบายการชำระเงิน"
"description": "ป้อนคำอธิบายการชำระเงิน",
"attach": "ส่งกลับตามเดิมเมื่อเรียกกลับ",
"extraParam": "JSON, พารามิเตอร์เฉพาะช่องทาง"
},
"default": {
"titlePrefix": "ทดสอบเกตเวย์"
@@ -304,7 +307,8 @@
"bizOrderNo": "กรุณาป้อนเลขที่คำสั่ง",
"title": "กรุณาป้อนชื่อการชำระเงิน",
"amount": "กรุณาป้อนจำนวนเงิน",
"gatewayPayType": "กรุณาเลือกประเภทเกตเวย์"
"gatewayPayType": "กรุณาเลือกประเภทเกตเวย์",
"extraParam": "พารามิเตอร์เพิ่มเติมต้องเป็น JSON ที่ถูกต้อง"
},
"msg": {
"signSuccess": "สร้างลายเซ็นแล้ว",

View File

@@ -60,7 +60,7 @@
"mchApp": "Thông tin người bán",
"param": "Thông số thanh toán",
"order": "Thông tin đặt hàng",
"preview": "Yêu cầu xem trước"
"advanced": "Tham số nâng cao"
},
"mode": {
"label": "Chế độ thanh toán",
@@ -69,11 +69,6 @@
"routeDesc": "Giải quyết khả năng và người bán kênh thông qua định tuyến kênh của ứng dụng",
"directDesc": "Quyết định bởi người bán kênh + khả năng; bỏ qua định tuyến kênh"
},
"preview": {
"title": "Yêu cầu JSON",
"signBtn": "Xem trước ký hiệu",
"empty": "Không có dữ liệu"
},
"field": {
"privateKey": "Khóa riêng của người bán",
"mchNo": "Thương gia",
@@ -89,7 +84,11 @@
"bizOrderNo": "Số thứ tự",
"amount": "Số tiền (Fen)",
"title": "Tiêu đề",
"description": "Sự miêu tả"
"description": "Sự miêu tả",
"returnUrl": "URL trả về đồng bộ",
"attach": "Tham số đính kèm",
"extraParam": "Tham số mở rộng",
"limitPay": "Giới hạn thanh toán"
},
"placeholder": {
"method": "Chọn phương thức thanh toán",
@@ -99,7 +98,11 @@
"notifyUrl": "https://...",
"expiredTime": "Mặc định 30 phút nếu trống",
"title": "Thứ tự kiểm tra",
"description": "Nhập mô tả thanh toán"
"description": "Nhập mô tả thanh toán",
"returnUrl": "https://...",
"attach": "Trả nguyên dạng khi callback",
"extraParam": "JSON, tham số riêng của kênh",
"limitPay": "Chọn hoặc nhập, vd: no_credit"
},
"default": {
"titlePrefix": "Trả tiền thử nghiệm"
@@ -145,7 +148,8 @@
"amount": "Vui lòng nhập số tiền",
"method": "Vui lòng chọn phương thức thanh toán",
"channelMchNo": "Vui lòng chọn một người bán kênh",
"capability": "Vui lòng chọn một khả năng"
"capability": "Vui lòng chọn một khả năng",
"extraParam": "Tham số mở rộng phải là JSON hợp lệ"
},
"msg": {
"signSuccess": "Đăng ký được tạo thành công",
@@ -232,7 +236,7 @@
"mchApp": "Thông tin merchant",
"order": "Thông tin đơn hàng",
"param": "Tham số mở rộng",
"preview": "Xem trước yêu cầu"
"advanced": "Tham số nâng cao"
},
"gatewayType": {
"label": "Loại cổng",
@@ -241,11 +245,6 @@
"cashierDesc": "Tạo trang đích quầy thu ngân, người dùng chọn phương thức thanh toán",
"aggregateDesc": "Tạo liên kết QR tổng hợp, một mã cho nhiều phương thức thanh toán"
},
"preview": {
"title": "JSON yêu cầu",
"signBtn": "Tạo xem trước chữ ký",
"empty": "Không có dữ liệu"
},
"field": {
"privateKey": "Khóa riêng merchant",
"mchNo": "Merchant",
@@ -257,7 +256,9 @@
"notifyUrl": "URL thông báo bất đồng bộ",
"returnUrl": "URL trả về đồng bộ",
"expiredTime": "Thời gian hết hạn",
"storeNo": "Số cửa hàng"
"storeNo": "Số cửa hàng",
"attach": "Tham số đính kèm",
"extraParam": "Tham số mở rộng"
},
"placeholder": {
"notifyUrl": "https://...",
@@ -265,7 +266,9 @@
"expiredTime": "Mặc định 30 phút",
"storeNo": "Số cửa hàng ngoại tuyến",
"title": "Đơn hàng thử nghiệm",
"description": "Nhập mô tả thanh toán"
"description": "Nhập mô tả thanh toán",
"attach": "Trả nguyên dạng khi callback",
"extraParam": "JSON, tham số riêng của kênh"
},
"default": {
"titlePrefix": "Test cổng thanh toán"
@@ -304,7 +307,8 @@
"bizOrderNo": "Vui lòng nhập số đơn hàng",
"title": "Vui lòng nhập tiêu đề thanh toán",
"amount": "Vui lòng nhập số tiền",
"gatewayPayType": "Vui lòng chọn loại cổng"
"gatewayPayType": "Vui lòng chọn loại cổng",
"extraParam": "Tham số mở rộng phải là JSON hợp lệ"
},
"msg": {
"signSuccess": "Đã tạo chữ ký",

View File

@@ -60,7 +60,7 @@
"mchApp": "商户信息",
"param": "支付参数",
"order": "订单信息",
"preview": "请求预览"
"advanced": "高级参数"
},
"mode": {
"label": "支付模式",
@@ -69,11 +69,6 @@
"routeDesc": "按应用通道路由解析通道商户与支付能力",
"directDesc": "由通道商户+支付能力直接决定支付实现,跳过通道路由"
},
"preview": {
"title": "请求参数 JSON",
"signBtn": "生成签名预览",
"empty": "暂无数据"
},
"field": {
"privateKey": "商户私钥",
"mchNo": "商户",
@@ -85,6 +80,10 @@
"openId": "OpenID",
"authCode": "付款码",
"notifyUrl": "异步通知地址",
"returnUrl": "同步跳转地址",
"attach": "商户附加参数",
"extraParam": "支付扩展参数",
"limitPay": "限制支付类型",
"expiredTime": "支付超时时间",
"bizOrderNo": "订单号",
"amount": "支付金额(分)",
@@ -97,6 +96,10 @@
"openId": "微信 jsapi/mini 场景必填",
"authCode": "付款码支付必填",
"notifyUrl": "https://...",
"returnUrl": "https://...",
"attach": "回调时原样返回",
"extraParam": "JSON 格式, 通道特有长尾参数",
"limitPay": "选择或输入限制项, 如 no_credit",
"expiredTime": "留空默认 30 分钟",
"title": "测试订单",
"description": "请输入支付描述"
@@ -145,7 +148,8 @@
"amount": "请输入支付金额",
"method": "请选择支付方式",
"channelMchNo": "请选择通道商户",
"capability": "请选择支付能力"
"capability": "请选择支付能力",
"extraParam": "扩展参数需为合法 JSON 格式"
},
"msg": {
"signSuccess": "签名生成成功",
@@ -232,7 +236,7 @@
"mchApp": "商户信息",
"order": "订单信息",
"param": "扩展参数",
"preview": "请求预览"
"advanced": "高级参数"
},
"gatewayType": {
"label": "网关支付类型",
@@ -241,11 +245,6 @@
"cashierDesc": "生成收银台落地页,用户在页面选择支付方式",
"aggregateDesc": "生成聚合扫码链接,一码多付自动识别"
},
"preview": {
"title": "请求参数 JSON",
"signBtn": "生成签名预览",
"empty": "暂无数据"
},
"field": {
"privateKey": "商户私钥",
"mchNo": "商户",
@@ -256,12 +255,16 @@
"description": "支付描述",
"notifyUrl": "异步通知地址",
"returnUrl": "同步跳转地址",
"attach": "商户附加参数",
"extraParam": "支付扩展参数",
"expiredTime": "支付超时时间",
"storeNo": "门店号"
},
"placeholder": {
"notifyUrl": "https://...",
"returnUrl": "https://...",
"attach": "回调时原样返回",
"extraParam": "JSON 格式, 通道特有长尾参数",
"expiredTime": "留空默认 30 分钟",
"storeNo": "线下经营门店号",
"title": "测试订单",
@@ -304,7 +307,8 @@
"bizOrderNo": "请输入订单号",
"title": "请输入支付标题",
"amount": "请输入支付金额",
"gatewayPayType": "请选择网关支付类型"
"gatewayPayType": "请选择网关支付类型",
"extraParam": "扩展参数需为合法 JSON 格式"
},
"msg": {
"signSuccess": "签名生成成功",

View File

@@ -60,7 +60,7 @@
"mchApp": "商户信息",
"param": "支付參數",
"order": "訂單信息",
"preview": "請求預覽"
"advanced": "進階參數"
},
"mode": {
"label": "支付模式",
@@ -69,11 +69,6 @@
"routeDesc": "按應用通道路由解析通道商户與支付能力",
"directDesc": "由通道商户+支付能力直接決定支付實現,跳過通道路由"
},
"preview": {
"title": "請求參數 JSON",
"signBtn": "生成簽名預覽",
"empty": "暫無數據"
},
"field": {
"privateKey": "商户私鑰",
"mchNo": "商户",
@@ -89,7 +84,11 @@
"bizOrderNo": "訂單號",
"amount": "支付金額(分)",
"title": "支付標題",
"description": "支付描述"
"description": "支付描述",
"returnUrl": "同步跳轉地址",
"attach": "商戶附加參數",
"extraParam": "支付擴展參數",
"limitPay": "限制支付類型"
},
"placeholder": {
"method": "請選擇支付方式",
@@ -99,7 +98,11 @@
"notifyUrl": "https://...",
"expiredTime": "留空默認 30 分鐘",
"title": "測試訂單",
"description": "請輸入支付描述"
"description": "請輸入支付描述",
"returnUrl": "https://...",
"attach": "回呼時原樣返回",
"extraParam": "JSON 格式, 通道特有長尾參數",
"limitPay": "選擇或輸入限制項, 如 no_credit"
},
"default": {
"titlePrefix": "測試支付"
@@ -145,7 +148,8 @@
"amount": "請輸入支付金額",
"method": "請選擇支付方式",
"channelMchNo": "請選擇通道商户",
"capability": "請選擇支付能力"
"capability": "請選擇支付能力",
"extraParam": "擴展參數需為合法 JSON 格式"
},
"msg": {
"signSuccess": "簽名生成成功",
@@ -232,7 +236,7 @@
"mchApp": "商戶資訊",
"order": "訂單資訊",
"param": "擴展參數",
"preview": "請求預覽"
"advanced": "進階參數"
},
"gatewayType": {
"label": "網關支付類型",
@@ -241,11 +245,6 @@
"cashierDesc": "生成收銀台落地頁,用戶喺頁面選擇支付方式",
"aggregateDesc": "生成聚合掃碼連結,一碼多付自動識別"
},
"preview": {
"title": "請求參數 JSON",
"signBtn": "生成簽名預覽",
"empty": "暫無數據"
},
"field": {
"privateKey": "商戶私鑰",
"mchNo": "商戶",
@@ -257,7 +256,9 @@
"notifyUrl": "異步通知地址",
"returnUrl": "同步跳轉地址",
"expiredTime": "支付超時時間",
"storeNo": "門店號"
"storeNo": "門店號",
"attach": "商戶附加參數",
"extraParam": "支付擴展參數"
},
"placeholder": {
"notifyUrl": "https://...",
@@ -265,7 +266,9 @@
"expiredTime": "留空預設 30 分鐘",
"storeNo": "線下經營門店號",
"title": "測試訂單",
"description": "請輸入支付描述"
"description": "請輸入支付描述",
"attach": "回呼時原樣返回",
"extraParam": "JSON 格式, 通道特有長尾參數"
},
"default": {
"titlePrefix": "測試網關支付"
@@ -304,7 +307,8 @@
"bizOrderNo": "請輸入訂單號",
"title": "請輸入支付標題",
"amount": "請輸入支付金額",
"gatewayPayType": "請選擇網關支付類型"
"gatewayPayType": "請選擇網關支付類型",
"extraParam": "擴展參數需為合法 JSON 格式"
},
"msg": {
"signSuccess": "簽名生成成功",

View File

@@ -60,7 +60,7 @@
"mchApp": "商戶資訊",
"param": "支付引數",
"order": "訂單資訊",
"preview": "請求預覽"
"advanced": "進階參數"
},
"mode": {
"label": "支付模式",
@@ -69,11 +69,6 @@
"routeDesc": "按應用通道路由解析通道商戶與支付能力",
"directDesc": "由通道商戶+支付能力直接決定支付實現,跳過通道路由"
},
"preview": {
"title": "請求引數 JSON",
"signBtn": "生成簽名預覽",
"empty": "暫無資料"
},
"field": {
"privateKey": "商戶私鑰",
"mchNo": "商戶",
@@ -89,7 +84,11 @@
"bizOrderNo": "訂單號",
"amount": "支付金額(分)",
"title": "支付標題",
"description": "支付描述"
"description": "支付描述",
"returnUrl": "同步跳轉地址",
"attach": "商戶附加參數",
"extraParam": "支付擴充參數",
"limitPay": "限制支付類型"
},
"placeholder": {
"method": "請選擇支付方式",
@@ -99,7 +98,11 @@
"notifyUrl": "https://...",
"expiredTime": "留空預設 30 分鐘",
"title": "測試訂單",
"description": "請輸入支付描述"
"description": "請輸入支付描述",
"returnUrl": "https://...",
"attach": "回呼時原樣返回",
"extraParam": "JSON 格式, 通道特有長尾參數",
"limitPay": "選擇或輸入限制項, 如 no_credit"
},
"default": {
"titlePrefix": "測試支付"
@@ -145,7 +148,8 @@
"amount": "請輸入支付金額",
"method": "請選擇支付方式",
"channelMchNo": "請選擇通道商戶",
"capability": "請選擇支付能力"
"capability": "請選擇支付能力",
"extraParam": "擴充參數需為合法 JSON 格式"
},
"msg": {
"signSuccess": "簽名生成成功",
@@ -232,7 +236,7 @@
"mchApp": "商戶資訊",
"order": "訂單資訊",
"param": "擴充參數",
"preview": "請求預覽"
"advanced": "進階參數"
},
"gatewayType": {
"label": "網關支付類型",
@@ -241,11 +245,6 @@
"cashierDesc": "產生收銀台落地頁,使用者在頁面選擇支付方式",
"aggregateDesc": "產生聚合掃碼連結,一碼多付自動識別"
},
"preview": {
"title": "請求參數 JSON",
"signBtn": "產生簽名預覽",
"empty": "暫無資料"
},
"field": {
"privateKey": "商戶私鑰",
"mchNo": "商戶",
@@ -257,7 +256,9 @@
"notifyUrl": "非同步通知地址",
"returnUrl": "同步跳轉地址",
"expiredTime": "支付逾時時間",
"storeNo": "門店號"
"storeNo": "門店號",
"attach": "商戶附加參數",
"extraParam": "支付擴充參數"
},
"placeholder": {
"notifyUrl": "https://...",
@@ -265,7 +266,9 @@
"expiredTime": "留空預設 30 分鐘",
"storeNo": "線下經營門店號",
"title": "測試訂單",
"description": "請輸入支付描述"
"description": "請輸入支付描述",
"attach": "回呼時原樣返回",
"extraParam": "JSON 格式, 通道特有長尾參數"
},
"default": {
"titlePrefix": "測試網關支付"
@@ -304,7 +307,8 @@
"bizOrderNo": "請輸入訂單號",
"title": "請輸入支付標題",
"amount": "請輸入支付金額",
"gatewayPayType": "請選擇網關支付類型"
"gatewayPayType": "請選擇網關支付類型",
"extraParam": "擴充參數需為合法 JSON 格式"
},
"msg": {
"signSuccess": "簽名產生成功",

View File

@@ -43,6 +43,21 @@
amount: [{ required: true, message: $t('payment.develop.gateway.rule.amount') }],
title: [{ required: true, message: $t('payment.develop.gateway.rule.title') }],
gatewayPayType: [{ required: true, message: $t('payment.develop.gateway.rule.gatewayPayType') }],
// 扩展参数: 非空时校验 JSON 合法性
extraParam: [
{
validator: async (_rule: any, value: string) => {
if (value && value.trim()) {
try {
JSON.parse(value);
} catch {
throw new Error($t('payment.develop.gateway.rule.extraParam'));
}
}
},
trigger: 'blur',
},
],
// 私钥存独立 ref, 用自定义校验挂到 form 上
privateKey: [
{
@@ -65,8 +80,10 @@
amount: 1,
gatewayPayType: 'cashier',
description: '',
// 高级参数(折叠面板中编辑, 默认空)
attach: '',
extraParam: '',
});
const signPreviewLoading = ref(false);
// ===== 下拉选项 =====
const mchNoOptions = ref<LabelValue[]>([]);
@@ -84,12 +101,6 @@
: $t('payment.develop.gateway.result.failModalTitle'),
);
// ===== 签名预览(请求预览卡内联展示) =====
const signPreview = reactive({
signStr: '',
sign: '',
});
/**
* 生成商户订单号(时间戳 + 随机串)
*/
@@ -234,37 +245,6 @@
return cleanPayload(payload);
}
/** 当前请求 JSON 预览(剔除空值) */
const requestPreview = computed(() => {
return JSON.stringify(buildPayload(), null, 2);
});
/** 生成签名预览(内联展示, 不弹结果) */
async function handleSignPreview() {
// 重入守卫: :loading DOM 更新有 nextTick 延迟, 连点会绕过按钮 disabled
if (signPreviewLoading.value) {
return;
}
try {
await formRef.value?.validateFields(['privateKey']);
} catch {
// 校验失败: 表单已显示错误提示
return;
}
signPreviewLoading.value = true;
try {
const { data } = await DevelopGatewayApi.sign({
param: buildPayload(),
privateKey: privateKey.value,
});
signPreview.signStr = data?.signStr ?? '';
signPreview.sign = data?.sign ?? '';
message.success($t('payment.develop.gateway.msg.signSuccess'));
} finally {
signPreviewLoading.value = false;
}
}
/** 复制文本到剪贴板 */
async function copyText(text: string, successKey = 'payment.develop.gateway.msg.copySuccess') {
if (!text) return;
@@ -309,8 +289,6 @@
return;
}
payload.sign = signRes?.sign;
signPreview.signStr = signRes?.signStr ?? '';
signPreview.sign = signRes?.sign ?? '';
// 直调网关预下单(无 Accesstoken, 完整商户契约)
try {
@@ -344,12 +322,12 @@
description: '',
notifyUrl: undefined,
returnUrl: undefined,
attach: undefined,
extraParam: undefined,
expiredTime: undefined,
storeNo: undefined,
});
mchAppOptions.value = [];
signPreview.signStr = '';
signPreview.sign = '';
genBizOrderNo();
}
@@ -485,14 +463,6 @@
/>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item :label="$t('payment.develop.gateway.field.returnUrl')" name="returnUrl">
<a-input
v-model:value="form.returnUrl"
:placeholder="$t('payment.develop.gateway.placeholder.returnUrl')"
/>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item :label="$t('payment.develop.gateway.field.expiredTime')" name="expiredTime">
<a-date-picker
@@ -579,64 +549,44 @@
</a-row>
</a-card>
<!-- 卡4: 请求预览(实时 JSON + 签名预览) -->
<a-card class="rounded-xl shadow-sm">
<template #title>
<div class="flex items-center gap-2">
<IconifyIcon icon="ant-design:code-outlined" class="text-amber-500" />
<span class="font-semibold">{{ $t('payment.develop.gateway.card.preview') }}</span>
</div>
</template>
<template #extra>
<a-button
size="small"
type="primary"
:loading="signPreviewLoading"
:disabled="signPreviewLoading"
@click="handleSignPreview"
>
<template #icon><IconifyIcon icon="ant-design:safety-certificate-outlined" /></template>
{{ $t('payment.develop.gateway.preview.signBtn') }}
</a-button>
</template>
<!-- 实时请求 JSON -->
<div class="mb-3 text-xs font-medium text-muted-foreground">
{{ $t('payment.develop.gateway.preview.title') }}
</div>
<pre class="code-box rounded-lg border border-border bg-muted/40 p-3 text-xs">{{ requestPreview }}</pre>
<!-- 签名预览结果 -->
<template v-if="signPreview.signStr || signPreview.sign">
<a-divider class="!my-3" />
<div class="preview-item mb-3">
<div class="mb-1 flex items-center justify-between">
<span class="text-xs font-medium text-foreground">
{{ $t('payment.develop.sign.field.signStr') }}
</span>
<a-button size="small" type="link" @click="copyText(signPreview.signStr)">
<IconifyIcon icon="ant-design:copy-outlined" />
</a-button>
<!-- 卡4: 高级参数(折叠, 默认收起) -->
<a-collapse :default-active-key="[]" class="advanced-collapse">
<a-collapse-panel key="advanced">
<template #header>
<div class="flex items-center gap-2">
<IconifyIcon icon="ant-design:setting-outlined" class="text-amber-500" />
<span class="font-semibold">{{ $t('payment.develop.gateway.card.advanced') }}</span>
</div>
<div class="code-box break-all rounded-lg border border-border bg-muted/40 p-3 text-xs">
{{ signPreview.signStr || $t('payment.develop.gateway.preview.empty') }}
</div>
</div>
<div class="preview-item">
<div class="mb-1 flex items-center justify-between">
<span class="text-xs font-medium text-foreground">
{{ $t('payment.develop.sign.field.signValue') }}
</span>
<a-button size="small" type="link" @click="copyText(signPreview.sign)">
<IconifyIcon icon="ant-design:copy-outlined" />
</a-button>
</div>
<div class="code-box break-all rounded-lg border border-border bg-muted/40 p-3 text-xs">
{{ signPreview.sign || $t('payment.develop.gateway.preview.empty') }}
</div>
</div>
</template>
</a-card>
</template>
<a-row :gutter="16">
<a-col :span="24">
<a-form-item :label="$t('payment.develop.gateway.field.returnUrl')" name="returnUrl">
<a-input
v-model:value="form.returnUrl"
:placeholder="$t('payment.develop.gateway.placeholder.returnUrl')"
/>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item :label="$t('payment.develop.gateway.field.attach')" name="attach">
<a-input
v-model:value="form.attach"
:placeholder="$t('payment.develop.gateway.placeholder.attach')"
/>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item :label="$t('payment.develop.gateway.field.extraParam')" name="extraParam">
<a-textarea
v-model:value="form.extraParam"
:rows="3"
:placeholder="$t('payment.develop.gateway.placeholder.extraParam')"
/>
</a-form-item>
</a-col>
</a-row>
</a-collapse-panel>
</a-collapse>
</div>
</a-col>
</a-row>
@@ -789,6 +739,14 @@
font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace;
}
// 高级参数折叠面板: 视觉与 a-card 保持一致
.advanced-collapse {
border: 1px solid hsl(var(--border));
border-radius: 12px;
background-color: hsl(var(--card));
box-shadow: 0 1px 3px hsl(var(--foreground) / 0.04);
}
// 底部悬浮居中胶囊栏: 固定在视口底部, 始终可点
.action-bar {
position: fixed;

View File

@@ -48,6 +48,21 @@
bizOrderNo: [{ required: true, message: $t('payment.develop.trade.rule.bizOrderNo') }],
amount: [{ required: true, message: $t('payment.develop.trade.rule.amount') }],
title: [{ required: true, message: $t('payment.develop.trade.rule.title') }],
// 扩展参数: 非空时校验 JSON 合法性
extraParam: [
{
validator: async (_rule: any, value: string) => {
if (value && value.trim()) {
try {
JSON.parse(value);
} catch {
throw new Error($t('payment.develop.trade.rule.extraParam'));
}
}
},
trigger: 'blur',
},
],
// 私钥存独立 ref, 用自定义校验挂到 form 上
privateKey: [
{
@@ -81,8 +96,14 @@
method: '',
capability: '',
description: '',
// 高级参数(折叠面板中编辑, 默认空)
attach: '',
extraParam: '',
limitPay: [],
});
const signPreviewLoading = ref(false);
// 限制支付类型候选(预置常用项, mode=tags 支持手输)
const limitPayOptions = [{ label: 'no_credit', value: 'no_credit' }];
// ===== 下拉选项 =====
const mchNoOptions = ref<LabelValue[]>([]);
@@ -114,12 +135,6 @@
return { ...raw, resTime: formatted || raw.resTime };
});
// ===== 签名预览(请求预览卡内联展示) =====
const signPreview = reactive({
signStr: '',
sign: '',
});
/**
* 生成商户订单号(时间戳 + 随机串)
*/
@@ -323,33 +338,6 @@
return cleanPayload(payload);
}
/** 当前请求 JSON 预览(剔除空值) */
const requestPreview = computed(() => {
return JSON.stringify(buildPayload(), null, 2);
});
/** 生成签名预览(内联展示, 不弹结果) */
async function handleSignPreview() {
try {
await formRef.value?.validateFields(['privateKey']);
} catch {
// 校验失败: 表单已显示错误提示
return;
}
signPreviewLoading.value = true;
try {
const { data } = await DevelopTradeApi.sign({
param: buildPayload(),
privateKey: privateKey.value,
});
signPreview.signStr = data?.signStr ?? '';
signPreview.sign = data?.sign ?? '';
message.success($t('payment.develop.trade.msg.signSuccess'));
} finally {
signPreviewLoading.value = false;
}
}
/** 复制文本到剪贴板 */
async function copyText(text: string, successKey = 'payment.develop.trade.msg.copySuccess') {
if (!text) return;
@@ -384,8 +372,6 @@
privateKey: privateKey.value,
});
payload.sign = signRes?.sign;
signPreview.signStr = signRes?.signStr ?? '';
signPreview.sign = signRes?.sign ?? '';
// 直调统一支付(无 Accesstoken, 完整商户契约)
// unipayPost 已将业务失败(code!=0)转为返回值, 仅网络层异常会 throw
@@ -424,14 +410,15 @@
authCode: undefined,
notifyUrl: undefined,
returnUrl: undefined,
attach: undefined,
extraParam: undefined,
limitPay: undefined,
expiredTime: undefined,
});
routeMode.value = 'route';
mchAppOptions.value = [];
channelMchNoOptions.value = [];
capabilityOptions.value = [];
signPreview.signStr = '';
signPreview.sign = '';
genBizOrderNo();
}
@@ -705,58 +692,54 @@
</a-row>
</a-card>
<!-- 卡4: 请求预览(实时 JSON + 签名预览) -->
<a-card class="rounded-xl shadow-sm">
<template #title>
<div class="flex items-center gap-2">
<IconifyIcon icon="ant-design:code-outlined" class="text-amber-500" />
<span class="font-semibold">{{ $t('payment.develop.trade.card.preview') }}</span>
</div>
</template>
<template #extra>
<a-button size="small" type="primary" :loading="signPreviewLoading" @click="handleSignPreview">
<template #icon><IconifyIcon icon="ant-design:safety-certificate-outlined" /></template>
{{ $t('payment.develop.trade.preview.signBtn') }}
</a-button>
</template>
<!-- 实时请求 JSON -->
<div class="mb-3 text-xs font-medium text-muted-foreground">
{{ $t('payment.develop.trade.preview.title') }}
</div>
<pre class="code-box rounded-lg border border-border bg-muted/40 p-3 text-xs">{{ requestPreview }}</pre>
<!-- 签名预览结果 -->
<template v-if="signPreview.signStr || signPreview.sign">
<a-divider class="!my-3" />
<div class="preview-item mb-3">
<div class="mb-1 flex items-center justify-between">
<span class="text-xs font-medium text-foreground">
{{ $t('payment.develop.sign.field.signStr') }}
</span>
<a-button size="small" type="link" @click="copyText(signPreview.signStr)">
<IconifyIcon icon="ant-design:copy-outlined" />
</a-button>
<!-- 卡4: 高级参数(折叠, 默认收起) -->
<a-collapse :default-active-key="[]" class="advanced-collapse">
<a-collapse-panel key="advanced">
<template #header>
<div class="flex items-center gap-2">
<IconifyIcon icon="ant-design:setting-outlined" class="text-amber-500" />
<span class="font-semibold">{{ $t('payment.develop.trade.card.advanced') }}</span>
</div>
<div class="code-box break-all rounded-lg border border-border bg-muted/40 p-3 text-xs">
{{ signPreview.signStr || $t('payment.develop.trade.preview.empty') }}
</div>
</div>
<div class="preview-item">
<div class="mb-1 flex items-center justify-between">
<span class="text-xs font-medium text-foreground">
{{ $t('payment.develop.sign.field.signValue') }}
</span>
<a-button size="small" type="link" @click="copyText(signPreview.sign)">
<IconifyIcon icon="ant-design:copy-outlined" />
</a-button>
</div>
<div class="code-box break-all rounded-lg border border-border bg-muted/40 p-3 text-xs">
{{ signPreview.sign || $t('payment.develop.trade.preview.empty') }}
</div>
</div>
</template>
</a-card>
</template>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item :label="$t('payment.develop.trade.field.returnUrl')" name="returnUrl">
<a-input
v-model:value="form.returnUrl"
:placeholder="$t('payment.develop.trade.placeholder.returnUrl')"
/>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item :label="$t('payment.develop.trade.field.attach')" name="attach">
<a-input
v-model:value="form.attach"
:placeholder="$t('payment.develop.trade.placeholder.attach')"
/>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item :label="$t('payment.develop.trade.field.limitPay')" name="limitPay">
<a-select
v-model:value="form.limitPay"
mode="tags"
:placeholder="$t('payment.develop.trade.placeholder.limitPay')"
:options="limitPayOptions"
/>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item :label="$t('payment.develop.trade.field.extraParam')" name="extraParam">
<a-textarea
v-model:value="form.extraParam"
:rows="3"
:placeholder="$t('payment.develop.trade.placeholder.extraParam')"
/>
</a-form-item>
</a-col>
</a-row>
</a-collapse-panel>
</a-collapse>
</div>
</a-col>
</a-row>
@@ -918,6 +901,14 @@
font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace;
}
// 高级参数折叠面板: 视觉与 a-card 保持一致
.advanced-collapse {
border: 1px solid hsl(var(--border));
border-radius: 12px;
background-color: hsl(var(--card));
box-shadow: 0 1px 3px hsl(var(--foreground) / 0.04);
}
// 底部悬浮居中胶囊栏: 固定在视口底部, 始终可点
.action-bar {
position: fixed;