mirror of
https://github.com/34892002/edgeKey.git
synced 2026-05-06 23:33:10 +08:00
feat: 自动部署
1. 支付网关组件化 2. 支持cf绑定git自动化部署
This commit is contained in:
@@ -3,7 +3,7 @@ import { badRequestError } from "../app-error";
|
|||||||
export function validatePaymentConfigInput(input: {
|
export function validatePaymentConfigInput(input: {
|
||||||
name?: string;
|
name?: string;
|
||||||
baseUrl?: string;
|
baseUrl?: string;
|
||||||
provider: "BEPUSDT" | "EPAY";
|
provider: string;
|
||||||
isEnabled?: boolean;
|
isEnabled?: boolean;
|
||||||
appSecret?: string;
|
appSecret?: string;
|
||||||
pid?: string;
|
pid?: string;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ export function listPaymentConfigRecords(prisma: PrismaClient) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getPaymentConfigRecord(prisma: PrismaClient, provider: "BEPUSDT" | "EPAY") {
|
export function getPaymentConfigRecord(prisma: PrismaClient, provider: string) {
|
||||||
return prisma.paymentConfig.findUnique({
|
return prisma.paymentConfig.findUnique({
|
||||||
where: { provider },
|
where: { provider },
|
||||||
});
|
});
|
||||||
@@ -14,7 +14,7 @@ export function getPaymentConfigRecord(prisma: PrismaClient, provider: "BEPUSDT"
|
|||||||
|
|
||||||
export function upsertPaymentConfigRecord(
|
export function upsertPaymentConfigRecord(
|
||||||
prisma: PrismaClient,
|
prisma: PrismaClient,
|
||||||
provider: "BEPUSDT" | "EPAY",
|
provider: string,
|
||||||
input: {
|
input: {
|
||||||
name: string;
|
name: string;
|
||||||
isEnabled: boolean;
|
isEnabled: boolean;
|
||||||
@@ -41,7 +41,7 @@ export function createPaymentLogRecord(
|
|||||||
prisma: PrismaClient,
|
prisma: PrismaClient,
|
||||||
input: {
|
input: {
|
||||||
orderId?: number;
|
orderId?: number;
|
||||||
provider: "BEPUSDT" | "EPAY";
|
provider: string;
|
||||||
orderNo?: string;
|
orderNo?: string;
|
||||||
paymentOrderNo?: string;
|
paymentOrderNo?: string;
|
||||||
eventType: string;
|
eventType: string;
|
||||||
|
|||||||
@@ -7,14 +7,14 @@ import { getAdminContext, logAdminOperation } from "../auth/service";
|
|||||||
import { notifyOrderPaid } from "../email/service";
|
import { notifyOrderPaid } from "../email/service";
|
||||||
import { getSiteSetting } from "../site/service";
|
import { getSiteSetting } from "../site/service";
|
||||||
import { createPaymentLogRecord, getPaymentConfigRecord, listPaymentConfigRecords, upsertPaymentConfigRecord } from "./repository";
|
import { createPaymentLogRecord, getPaymentConfigRecord, listPaymentConfigRecords, upsertPaymentConfigRecord } from "./repository";
|
||||||
import type { PaymentMethodItem } from "./types";
|
import type { PaymentMethodItem, PaymentProvider } from "./types";
|
||||||
import type { PaymentConfigValue } from "./types";
|
import type { PaymentConfigValue } from "./types";
|
||||||
import { createBepusdtAdapter } from "./bepusdt";
|
import { createBepusdtAdapter } from "./bepusdt";
|
||||||
import { createEpayAdapter } from "./epay";
|
import { createEpayAdapter } from "./epay";
|
||||||
import { deliverOrder } from "../delivery/service";
|
import { deliverOrder } from "../delivery/service";
|
||||||
import { findOrderRecord, updateOrderPayment } from "../order/repository";
|
import { findOrderRecord, updateOrderPayment } from "../order/repository";
|
||||||
|
|
||||||
const defaultPaymentConfigs: Record<"BEPUSDT" | "EPAY", PaymentConfigValue> = {
|
const defaultPaymentConfigs: Record<PaymentProvider, PaymentConfigValue> = {
|
||||||
BEPUSDT: {
|
BEPUSDT: {
|
||||||
provider: "BEPUSDT",
|
provider: "BEPUSDT",
|
||||||
name: "USDT",
|
name: "USDT",
|
||||||
@@ -41,7 +41,7 @@ function getPaymentContext() {
|
|||||||
return getContext<{ prisma: PrismaClient }>();
|
return getContext<{ prisma: PrismaClient }>();
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizePaymentConfig(record: Awaited<ReturnType<typeof getPaymentConfigRecord>>, provider: "BEPUSDT" | "EPAY"): PaymentConfigValue {
|
function normalizePaymentConfig(record: Awaited<ReturnType<typeof getPaymentConfigRecord>>, provider: PaymentProvider): PaymentConfigValue {
|
||||||
const defaults = defaultPaymentConfigs[provider];
|
const defaults = defaultPaymentConfigs[provider];
|
||||||
if (!record) {
|
if (!record) {
|
||||||
return defaults;
|
return defaults;
|
||||||
@@ -69,7 +69,7 @@ export async function listEnabledPaymentMethods(prisma?: PrismaClient): Promise<
|
|||||||
const client = prisma ?? getPaymentContext().prisma;
|
const client = prisma ?? getPaymentContext().prisma;
|
||||||
const records = await listPaymentConfigRecords(client);
|
const records = await listPaymentConfigRecords(client);
|
||||||
|
|
||||||
return (["BEPUSDT", "EPAY"] as const).map((provider) => {
|
return (Object.keys(defaultPaymentConfigs) as PaymentProvider[]).map((provider) => {
|
||||||
const record = records.find((item) => item.provider === provider);
|
const record = records.find((item) => item.provider === provider);
|
||||||
const value = normalizePaymentConfig(record ?? null, provider);
|
const value = normalizePaymentConfig(record ?? null, provider);
|
||||||
return {
|
return {
|
||||||
@@ -81,17 +81,15 @@ export async function listEnabledPaymentMethods(prisma?: PrismaClient): Promise<
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getPaymentConfigs(prisma?: PrismaClient) {
|
export async function getPaymentConfigs(prisma?: PrismaClient): Promise<Record<string, PaymentConfigValue>> {
|
||||||
const client = prisma ?? getPaymentContext().prisma;
|
const client = prisma ?? getPaymentContext().prisma;
|
||||||
const [bepusdt, epay] = await Promise.all([
|
const records = await listPaymentConfigRecords(client);
|
||||||
getPaymentConfigRecord(client, "BEPUSDT"),
|
const result: Record<string, PaymentConfigValue> = {};
|
||||||
getPaymentConfigRecord(client, "EPAY"),
|
for (const provider of Object.keys(defaultPaymentConfigs) as PaymentProvider[]) {
|
||||||
]);
|
const record = records.find((r) => r.provider === provider) ?? null;
|
||||||
|
result[provider] = normalizePaymentConfig(record, provider);
|
||||||
return {
|
}
|
||||||
BEPUSDT: normalizePaymentConfig(bepusdt, "BEPUSDT"),
|
return result;
|
||||||
EPAY: normalizePaymentConfig(epay, "EPAY"),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function savePaymentConfig(input: PaymentConfigValue) {
|
export async function savePaymentConfig(input: PaymentConfigValue) {
|
||||||
@@ -200,7 +198,7 @@ export async function createPaymentForOrder(orderNo: string, prisma?: PrismaClie
|
|||||||
const notifyUrl = resolveCallbackUrl(
|
const notifyUrl = resolveCallbackUrl(
|
||||||
baseOrigin,
|
baseOrigin,
|
||||||
config.notifyUrl,
|
config.notifyUrl,
|
||||||
order.paymentProvider === "BEPUSDT" ? "/api/payments/bepusdt/notify" : "/api/payments/epay/notify",
|
defaultPaymentConfigs[order.paymentProvider as PaymentProvider]?.notifyUrl ?? "/api/payments/notify",
|
||||||
);
|
);
|
||||||
const returnUrl = resolveCallbackUrl(
|
const returnUrl = resolveCallbackUrl(
|
||||||
baseOrigin,
|
baseOrigin,
|
||||||
@@ -272,7 +270,7 @@ function sanitizePaymentPayload(payload?: Record<string, unknown>) {
|
|||||||
|
|
||||||
async function createNotifyLog(prisma: PrismaClient, input: {
|
async function createNotifyLog(prisma: PrismaClient, input: {
|
||||||
orderId?: number;
|
orderId?: number;
|
||||||
provider: "BEPUSDT" | "EPAY";
|
provider: PaymentProvider;
|
||||||
orderNo?: string;
|
orderNo?: string;
|
||||||
paymentOrderNo?: string;
|
paymentOrderNo?: string;
|
||||||
eventType?: string;
|
eventType?: string;
|
||||||
@@ -294,7 +292,7 @@ async function createNotifyLog(prisma: PrismaClient, input: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function writePaymentNotifyDiagnostic(input: {
|
function writePaymentNotifyDiagnostic(input: {
|
||||||
provider: "BEPUSDT" | "EPAY";
|
provider: PaymentProvider;
|
||||||
source: string;
|
source: string;
|
||||||
reason: string;
|
reason: string;
|
||||||
payload?: Record<string, unknown>;
|
payload?: Record<string, unknown>;
|
||||||
@@ -322,7 +320,7 @@ function writePaymentNotifyDiagnostic(input: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function handlePaymentNotify(
|
export async function handlePaymentNotify(
|
||||||
provider: "BEPUSDT" | "EPAY",
|
provider: PaymentProvider,
|
||||||
payload: Record<string, string>,
|
payload: Record<string, string>,
|
||||||
prisma: PrismaClient,
|
prisma: PrismaClient,
|
||||||
source: string,
|
source: string,
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
|
export type PaymentProvider = "BEPUSDT" | "EPAY";
|
||||||
|
|
||||||
export interface PaymentMethodItem {
|
export interface PaymentMethodItem {
|
||||||
provider: "BEPUSDT" | "EPAY";
|
provider: PaymentProvider;
|
||||||
label: string;
|
label: string;
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
baseUrl?: string;
|
baseUrl?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PaymentConfigValue {
|
export interface PaymentConfigValue {
|
||||||
provider: "BEPUSDT" | "EPAY";
|
provider: PaymentProvider;
|
||||||
name: string;
|
name: string;
|
||||||
isEnabled: boolean;
|
isEnabled: boolean;
|
||||||
baseUrl: string;
|
baseUrl: string;
|
||||||
@@ -16,4 +18,4 @@ export interface PaymentConfigValue {
|
|||||||
key?: string;
|
key?: string;
|
||||||
notifyUrl?: string;
|
notifyUrl?: string;
|
||||||
returnUrl?: string;
|
returnUrl?: string;
|
||||||
}
|
}
|
||||||
@@ -7,9 +7,9 @@
|
|||||||
"db:seed": "bunx wrangler d1 execute DB --local --file=./scripts/seed.sql -y",
|
"db:seed": "bunx wrangler d1 execute DB --local --file=./scripts/seed.sql -y",
|
||||||
"db:generate": "prisma generate",
|
"db:generate": "prisma generate",
|
||||||
"db:studio": "prisma studio",
|
"db:studio": "prisma studio",
|
||||||
"db:migrations:remote": "wrangler d1 migrations apply DB --remote",
|
"db:migrations:remote": "wrangler d1 migrations apply DB --remote ${D1_ID:+--database-id $D1_ID}",
|
||||||
"db:migrations:local": "wrangler d1 migrations apply DB --local",
|
"db:migrations:local": "wrangler d1 migrations apply DB --local",
|
||||||
"db:seed:remote": "wrangler d1 execute DB --remote --file=./scripts/seed.sql -y",
|
"db:seed:remote": "wrangler d1 execute DB --remote --file=./scripts/seed.sql -y ${D1_ID:+--database-id $D1_ID}",
|
||||||
"verify:payments": "bun run scripts/verify-payment-adapters.ts",
|
"verify:payments": "bun run scripts/verify-payment-adapters.ts",
|
||||||
"verify:payment-notify": "bun run scripts/verify-payment-notify.ts",
|
"verify:payment-notify": "bun run scripts/verify-payment-notify.ts",
|
||||||
"deploy": "bun run db:migrations:remote && bun run db:seed:remote && wrangler deploy",
|
"deploy": "bun run db:migrations:remote && bun run db:seed:remote && wrangler deploy",
|
||||||
|
|||||||
@@ -392,7 +392,7 @@
|
|||||||
<th>#</th>
|
<th>#</th>
|
||||||
<th>时间</th>
|
<th>时间</th>
|
||||||
<th>分类</th>
|
<th>分类</th>
|
||||||
<th>API服务商</th>
|
<th>邮箱名称</th>
|
||||||
<th>场景</th>
|
<th>场景</th>
|
||||||
<th>状态</th>
|
<th>状态</th>
|
||||||
<th>收件人</th>
|
<th>收件人</th>
|
||||||
@@ -407,7 +407,7 @@
|
|||||||
<th>{{ index + 1 }}</th>
|
<th>{{ index + 1 }}</th>
|
||||||
<td class="whitespace-nowrap">{{ formatDate(log.createdAt) }}</td>
|
<td class="whitespace-nowrap">{{ formatDate(log.createdAt) }}</td>
|
||||||
<td class="whitespace-nowrap">{{ getChannelLabel(log.provider) }}</td>
|
<td class="whitespace-nowrap">{{ getChannelLabel(log.provider) }}</td>
|
||||||
<td class="whitespace-nowrap">{{ log.apiProvider || '-' }}</td>
|
<td class="whitespace-nowrap">{{ configs.find(c => c.provider === log.provider)?.name || '-' }}</td>
|
||||||
<td class="whitespace-nowrap">{{ getSceneLabel(log.scene) }}</td>
|
<td class="whitespace-nowrap">{{ getSceneLabel(log.scene) }}</td>
|
||||||
<td>
|
<td>
|
||||||
<span class="badge whitespace-nowrap" :class="log.status === 'SUCCESS' ? 'badge-success' : 'badge-error'">
|
<span class="badge whitespace-nowrap" :class="log.status === 'SUCCESS' ? 'badge-success' : 'badge-error'">
|
||||||
|
|||||||
@@ -1,17 +1,30 @@
|
|||||||
<template>
|
<template>
|
||||||
<section class="space-y-6">
|
<section class="space-y-6">
|
||||||
<div class="alert alert-info">
|
<div class="alert alert-info">
|
||||||
<span>启用支付前,请先前往“站点设置”配置网站地址,否则无法获取支付结果。</span>
|
<span>启用支付前,请先前往"站点设置"配置网站地址,否则无法获取支付结果。</span>
|
||||||
</div>
|
</div>
|
||||||
<PaymentConfigCard provider="BEPUSDT" title="BEpusdt / USDT" :initial-value="configs.BEPUSDT" />
|
<div role="tablist" class="tabs tabs-border">
|
||||||
<PaymentConfigCard provider="EPAY" title="Epay / 聚合支付" :initial-value="configs.EPAY" />
|
<a role="tab" class="tab" :class="{ 'tab-active': activeTab === 'BEPUSDT' }" @click="activeTab = 'BEPUSDT'">
|
||||||
|
BEpusdt / USDT
|
||||||
|
<span v-if="localConfigs.BEPUSDT?.isEnabled" class="ml-1.5 inline-block w-2 h-2 rounded-full bg-success"></span>
|
||||||
|
</a>
|
||||||
|
<a role="tab" class="tab" :class="{ 'tab-active': activeTab === 'EPAY' }" @click="activeTab = 'EPAY'">
|
||||||
|
Epay / 聚合支付
|
||||||
|
<span v-if="localConfigs.EPAY?.isEnabled" class="ml-1.5 inline-block w-2 h-2 rounded-full bg-success"></span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<PaymentConfigCard v-if="activeTab === 'BEPUSDT'" provider="BEPUSDT" title="BEpusdt / USDT" :initial-value="localConfigs.BEPUSDT" @saved="localConfigs.BEPUSDT = $event" />
|
||||||
|
<PaymentConfigCard v-if="activeTab === 'EPAY'" provider="EPAY" title="Epay / 聚合支付" :initial-value="localConfigs.EPAY" @saved="localConfigs.EPAY = $event" />
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { ref, reactive } from "vue";
|
||||||
import { useData } from "vike-vue/useData";
|
import { useData } from "vike-vue/useData";
|
||||||
import PaymentConfigCard from "./PaymentConfigCard.vue";
|
import PaymentConfigCard from "./PaymentConfigCard.vue";
|
||||||
import type { Data } from "./+data";
|
import type { Data } from "./+data";
|
||||||
|
|
||||||
const { configs } = useData<Data>();
|
const { configs } = useData<Data>();
|
||||||
</script>
|
const activeTab = ref("BEPUSDT");
|
||||||
|
const localConfigs = reactive({ ...configs });
|
||||||
|
</script>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { getPaymentConfigs } from "../../../modules/payment/service";
|
import { getPaymentConfigs } from "../../../modules/payment/service";
|
||||||
|
|
||||||
export type Data = ReturnType<typeof data>;
|
export type Data = Awaited<ReturnType<typeof data>>;
|
||||||
|
|
||||||
export async function data(pageContext: {
|
export async function data(pageContext: {
|
||||||
prisma: import("../../../generated/prisma/client").PrismaClient;
|
prisma: import("../../../generated/prisma/client").PrismaClient;
|
||||||
|
|||||||
@@ -23,27 +23,7 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="provider === 'BEPUSDT'" class="grid gap-4 md:grid-cols-2">
|
<component :is="formMap[provider]" v-model="extraFields" />
|
||||||
<label class="flex flex-col gap-1.5">
|
|
||||||
<span class="label-text font-medium">App ID</span>
|
|
||||||
<input v-model="form.appId" class="input input-bordered w-full" />
|
|
||||||
</label>
|
|
||||||
<label class="flex flex-col gap-1.5">
|
|
||||||
<span class="label-text font-medium">App Secret</span>
|
|
||||||
<input v-model="form.appSecret" class="input input-bordered w-full" />
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-else class="grid gap-4 md:grid-cols-2">
|
|
||||||
<label class="flex flex-col gap-1.5">
|
|
||||||
<span class="label-text font-medium">PID</span>
|
|
||||||
<input v-model="form.pid" class="input input-bordered w-full" />
|
|
||||||
</label>
|
|
||||||
<label class="flex flex-col gap-1.5">
|
|
||||||
<span class="label-text font-medium">Key</span>
|
|
||||||
<input v-model="form.key" class="input input-bordered w-full" />
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="grid gap-4 md:grid-cols-2">
|
<div class="grid gap-4 md:grid-cols-2">
|
||||||
<label class="flex flex-col gap-1.5">
|
<label class="flex flex-col gap-1.5">
|
||||||
@@ -56,10 +36,6 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p v-if="provider === 'EPAY'" class="text-xs text-base-content/60">
|
|
||||||
`Notify URL` 和 `Return URL` 支持填写相对路径或完整 URL;`Return URL` 支持 `{orderNo}`、`{token}` 占位符。
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div class="flex items-center gap-3">
|
<div class="flex items-center gap-3">
|
||||||
<button class="btn btn-primary" :disabled="saving" @click="handleSave">
|
<button class="btn btn-primary" :disabled="saving" @click="handleSave">
|
||||||
{{ saving ? '保存中...' : '保存配置' }}
|
{{ saving ? '保存中...' : '保存配置' }}
|
||||||
@@ -72,15 +48,22 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { normalizeTelefuncError } from "../../../lib/app-error";
|
|
||||||
import { reactive, ref } from "vue";
|
import { reactive, ref } from "vue";
|
||||||
|
import { normalizeTelefuncError } from "../../../lib/app-error";
|
||||||
import { onSavePaymentConfig } from "./savePaymentConfig.telefunc";
|
import { onSavePaymentConfig } from "./savePaymentConfig.telefunc";
|
||||||
|
import BEpusdtForm from "./forms/BEpusdtForm.vue";
|
||||||
|
import EpayForm from "./forms/EpayForm.vue";
|
||||||
|
import type { PaymentProvider } from "../../../modules/payment/types";
|
||||||
|
|
||||||
|
const formMap = { BEPUSDT: BEpusdtForm, EPAY: EpayForm };
|
||||||
|
|
||||||
|
const emit = defineEmits<{ saved: [value: typeof props.initialValue] }>();
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
provider: "BEPUSDT" | "EPAY";
|
provider: PaymentProvider;
|
||||||
title: string;
|
title: string;
|
||||||
initialValue: {
|
initialValue: {
|
||||||
provider: "BEPUSDT" | "EPAY";
|
provider: PaymentProvider;
|
||||||
name: string;
|
name: string;
|
||||||
isEnabled: boolean;
|
isEnabled: boolean;
|
||||||
baseUrl: string;
|
baseUrl: string;
|
||||||
@@ -94,18 +77,19 @@ const props = defineProps<{
|
|||||||
}>();
|
}>();
|
||||||
|
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
provider: props.provider,
|
|
||||||
name: props.initialValue?.name ?? (props.provider === 'BEPUSDT' ? 'USDT' : '聚合支付'),
|
name: props.initialValue?.name ?? (props.provider === 'BEPUSDT' ? 'USDT' : '聚合支付'),
|
||||||
isEnabled: props.initialValue?.isEnabled ?? false,
|
isEnabled: props.initialValue?.isEnabled ?? false,
|
||||||
baseUrl: props.initialValue?.baseUrl ?? '',
|
baseUrl: props.initialValue?.baseUrl ?? '',
|
||||||
appId: props.initialValue?.appId ?? '',
|
|
||||||
appSecret: props.initialValue?.appSecret ?? '',
|
|
||||||
pid: props.initialValue?.pid ?? '',
|
|
||||||
key: props.initialValue?.key ?? '',
|
|
||||||
notifyUrl: props.initialValue?.notifyUrl ?? '',
|
notifyUrl: props.initialValue?.notifyUrl ?? '',
|
||||||
returnUrl: props.initialValue?.returnUrl ?? '',
|
returnUrl: props.initialValue?.returnUrl ?? '',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const extraFields = reactive(
|
||||||
|
props.provider === 'BEPUSDT'
|
||||||
|
? { appId: props.initialValue?.appId ?? '', appSecret: props.initialValue?.appSecret ?? '' }
|
||||||
|
: { pid: props.initialValue?.pid ?? '', key: props.initialValue?.key ?? '' }
|
||||||
|
);
|
||||||
|
|
||||||
const saving = ref(false);
|
const saving = ref(false);
|
||||||
const saved = ref(false);
|
const saved = ref(false);
|
||||||
const errorMessage = ref('');
|
const errorMessage = ref('');
|
||||||
@@ -114,23 +98,26 @@ async function handleSave() {
|
|||||||
saving.value = true;
|
saving.value = true;
|
||||||
saved.value = false;
|
saved.value = false;
|
||||||
errorMessage.value = '';
|
errorMessage.value = '';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await onSavePaymentConfig({ ...form });
|
const result = await onSavePaymentConfig({ provider: props.provider, ...form, ...extraFields });
|
||||||
form.name = result.name;
|
form.name = result.name;
|
||||||
form.isEnabled = result.isEnabled;
|
form.isEnabled = result.isEnabled;
|
||||||
form.baseUrl = result.baseUrl;
|
form.baseUrl = result.baseUrl;
|
||||||
form.appId = result.appId ?? '';
|
|
||||||
form.appSecret = result.appSecret ?? '';
|
|
||||||
form.pid = result.pid ?? '';
|
|
||||||
form.key = result.key ?? '';
|
|
||||||
form.notifyUrl = result.notifyUrl ?? '';
|
form.notifyUrl = result.notifyUrl ?? '';
|
||||||
form.returnUrl = result.returnUrl ?? '';
|
form.returnUrl = result.returnUrl ?? '';
|
||||||
|
if (props.provider === 'BEPUSDT') {
|
||||||
|
(extraFields as any).appId = result.appId ?? '';
|
||||||
|
(extraFields as any).appSecret = result.appSecret ?? '';
|
||||||
|
} else {
|
||||||
|
(extraFields as any).pid = result.pid ?? '';
|
||||||
|
(extraFields as any).key = result.key ?? '';
|
||||||
|
}
|
||||||
saved.value = true;
|
saved.value = true;
|
||||||
|
emit('saved', { provider: props.provider, ...form, ...extraFields });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
errorMessage.value = normalizeTelefuncError(error, '保存失败');
|
errorMessage.value = normalizeTelefuncError(error, '保存失败');
|
||||||
} finally {
|
} finally {
|
||||||
saving.value = false;
|
saving.value = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
16
pages/admin/payments/forms/BEpusdtForm.vue
Normal file
16
pages/admin/payments/forms/BEpusdtForm.vue
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<template>
|
||||||
|
<div class="grid gap-4 md:grid-cols-2">
|
||||||
|
<label class="flex flex-col gap-1.5">
|
||||||
|
<span class="label-text font-medium">App ID</span>
|
||||||
|
<input v-model="modelValue.appId" class="input input-bordered w-full" />
|
||||||
|
</label>
|
||||||
|
<label class="flex flex-col gap-1.5">
|
||||||
|
<span class="label-text font-medium">App Secret</span>
|
||||||
|
<input v-model="modelValue.appSecret" class="input input-bordered w-full" />
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
defineProps<{ modelValue: Record<string, string> }>();
|
||||||
|
</script>
|
||||||
19
pages/admin/payments/forms/EpayForm.vue
Normal file
19
pages/admin/payments/forms/EpayForm.vue
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<template>
|
||||||
|
<div class="grid gap-4 md:grid-cols-2">
|
||||||
|
<label class="flex flex-col gap-1.5">
|
||||||
|
<span class="label-text font-medium">PID</span>
|
||||||
|
<input v-model="modelValue.pid" class="input input-bordered w-full" />
|
||||||
|
</label>
|
||||||
|
<label class="flex flex-col gap-1.5">
|
||||||
|
<span class="label-text font-medium">Key</span>
|
||||||
|
<input v-model="modelValue.key" class="input input-bordered w-full" />
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<p class="text-xs text-base-content/60">
|
||||||
|
`Notify URL` 和 `Return URL` 支持填写相对路径或完整 URL;`Return URL` 支持 `{orderNo}`、`{token}` 占位符。
|
||||||
|
</p>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
defineProps<{ modelValue: Record<string, string> }>();
|
||||||
|
</script>
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
import { assertAdminAccess } from "../../../modules/auth/service";
|
import { assertAdminAccess } from "../../../modules/auth/service";
|
||||||
import { savePaymentConfig } from "../../../modules/payment/service";
|
import { savePaymentConfig } from "../../../modules/payment/service";
|
||||||
|
import type { PaymentProvider } from "../../../modules/payment/types";
|
||||||
|
|
||||||
export async function onSavePaymentConfig(input: {
|
export async function onSavePaymentConfig(input: {
|
||||||
provider: "BEPUSDT" | "EPAY";
|
provider: PaymentProvider;
|
||||||
name: string;
|
name: string;
|
||||||
isEnabled: boolean;
|
isEnabled: boolean;
|
||||||
baseUrl: string;
|
baseUrl: string;
|
||||||
|
|||||||
@@ -48,10 +48,7 @@ enum ContactType {
|
|||||||
OTHER
|
OTHER
|
||||||
}
|
}
|
||||||
|
|
||||||
enum PaymentProvider {
|
|
||||||
BEPUSDT
|
|
||||||
EPAY
|
|
||||||
}
|
|
||||||
|
|
||||||
enum EmailChannel {
|
enum EmailChannel {
|
||||||
API
|
API
|
||||||
@@ -208,7 +205,7 @@ model Order {
|
|||||||
contactType ContactType @default(EMAIL)
|
contactType ContactType @default(EMAIL)
|
||||||
contactValue String?
|
contactValue String?
|
||||||
buyerNote String?
|
buyerNote String?
|
||||||
paymentProvider PaymentProvider
|
paymentProvider String
|
||||||
paymentChannel String?
|
paymentChannel String?
|
||||||
paymentOrderNo String?
|
paymentOrderNo String?
|
||||||
status OrderStatus @default(PENDING)
|
status OrderStatus @default(PENDING)
|
||||||
@@ -245,7 +242,7 @@ model OrderDelivery {
|
|||||||
|
|
||||||
model PaymentConfig {
|
model PaymentConfig {
|
||||||
id Int @id @default(autoincrement())
|
id Int @id @default(autoincrement())
|
||||||
provider PaymentProvider @unique
|
provider String @unique
|
||||||
name String
|
name String
|
||||||
isEnabled Boolean @default(false)
|
isEnabled Boolean @default(false)
|
||||||
configJson String
|
configJson String
|
||||||
@@ -256,7 +253,7 @@ model PaymentConfig {
|
|||||||
model PaymentLog {
|
model PaymentLog {
|
||||||
id Int @id @default(autoincrement())
|
id Int @id @default(autoincrement())
|
||||||
orderId Int?
|
orderId Int?
|
||||||
provider PaymentProvider
|
provider String
|
||||||
orderNo String?
|
orderNo String?
|
||||||
paymentOrderNo String?
|
paymentOrderNo String?
|
||||||
eventType String
|
eventType String
|
||||||
|
|||||||
Reference in New Issue
Block a user