mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-10-28 02:41:37 +08:00
feat(payment): add EasyPay dual-mode support (redirect + API)
- Backend: EasyPay provider supports paymentMode config (redirect/api) - redirect: builds submit.php signed URL, browser redirects to hosted page - api: calls mapi.php, returns payurl/qrcode (existing behavior) - Add TypeEasyPay payment type constant - Fix notify/return URL fallback to instance config - Handle empty TradeNo for redirect mode (SetNillable) - Frontend: add paymentMode dropdown in EasyPay provider config - Auto-update supported_types when paymentMode changes - Add easypay icon/color in PaymentMethodSelector - Add i18n for easypay, paymentMode labels - Rename "启用的支付方式" to "服务商" - Migration 098: clean easypay from ENABLED_PAYMENT_TYPES
This commit is contained in:
@@ -23,6 +23,11 @@ const (
|
|||||||
easypayCodeSuccess = 1
|
easypayCodeSuccess = 1
|
||||||
easypayStatusPaid = 1
|
easypayStatusPaid = 1
|
||||||
easypayHTTPTimeout = 10 * time.Second
|
easypayHTTPTimeout = 10 * time.Second
|
||||||
|
|
||||||
|
// EasyPayModeRedirect uses submit.php — browser redirects to EasyPay hosted page.
|
||||||
|
EasyPayModeRedirect = "redirect"
|
||||||
|
// EasyPayModeAPI uses mapi.php — server calls API, returns payurl/qrcode.
|
||||||
|
EasyPayModeAPI = "api"
|
||||||
)
|
)
|
||||||
|
|
||||||
// EasyPay implements payment.Provider for the EasyPay aggregation platform.
|
// EasyPay implements payment.Provider for the EasyPay aggregation platform.
|
||||||
@@ -47,17 +52,58 @@ func NewEasyPay(instanceID string, config map[string]string) (*EasyPay, error) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *EasyPay) Name() string { return "EasyPay" }
|
func (e *EasyPay) Name() string { return "EasyPay" }
|
||||||
func (e *EasyPay) ProviderKey() string { return "easypay" }
|
func (e *EasyPay) ProviderKey() string { return "easypay" }
|
||||||
func (e *EasyPay) SupportedTypes() []payment.PaymentType {
|
func (e *EasyPay) SupportedTypes() []payment.PaymentType {
|
||||||
|
if e.config["paymentMode"] == EasyPayModeRedirect {
|
||||||
|
return []payment.PaymentType{payment.TypeEasyPay}
|
||||||
|
}
|
||||||
return []payment.PaymentType{payment.TypeAlipay, payment.TypeWxpay}
|
return []payment.PaymentType{payment.TypeAlipay, payment.TypeWxpay}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *EasyPay) CreatePayment(ctx context.Context, req payment.CreatePaymentRequest) (*payment.CreatePaymentResponse, error) {
|
func (e *EasyPay) CreatePayment(ctx context.Context, req payment.CreatePaymentRequest) (*payment.CreatePaymentResponse, error) {
|
||||||
|
if e.config["paymentMode"] == EasyPayModeRedirect {
|
||||||
|
return e.createRedirectPayment(req)
|
||||||
|
}
|
||||||
|
return e.createAPIPayment(ctx, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
// createRedirectPayment builds a submit.php URL for browser redirect.
|
||||||
|
// No server-side API call — the user is redirected to EasyPay's hosted page.
|
||||||
|
// TradeNo is empty; it arrives via the notify callback after payment.
|
||||||
|
func (e *EasyPay) createRedirectPayment(req payment.CreatePaymentRequest) (*payment.CreatePaymentResponse, error) {
|
||||||
|
notifyURL, returnURL := e.resolveURLs(req)
|
||||||
|
payType := req.PaymentType
|
||||||
|
if payType == "easypay" {
|
||||||
|
payType = "alipay" // default for hosted page; user can switch on EasyPay's UI
|
||||||
|
}
|
||||||
|
params := map[string]string{
|
||||||
|
"pid": e.config["pid"], "type": payType,
|
||||||
|
"out_trade_no": req.OrderID, "notify_url": notifyURL,
|
||||||
|
"return_url": returnURL, "name": req.Subject,
|
||||||
|
"money": req.Amount,
|
||||||
|
}
|
||||||
|
if cid := e.resolveCID(payType); cid != "" {
|
||||||
|
params["cid"] = cid
|
||||||
|
}
|
||||||
|
params["sign"] = easyPaySign(params, e.config["pkey"])
|
||||||
|
params["sign_type"] = "MD5"
|
||||||
|
|
||||||
|
q := url.Values{}
|
||||||
|
for k, v := range params {
|
||||||
|
q.Set(k, v)
|
||||||
|
}
|
||||||
|
payURL := e.config["apiBase"] + "/submit.php?" + q.Encode()
|
||||||
|
return &payment.CreatePaymentResponse{PayURL: payURL}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// createAPIPayment calls mapi.php to get payurl/qrcode (existing behavior).
|
||||||
|
func (e *EasyPay) createAPIPayment(ctx context.Context, req payment.CreatePaymentRequest) (*payment.CreatePaymentResponse, error) {
|
||||||
|
notifyURL, returnURL := e.resolveURLs(req)
|
||||||
params := map[string]string{
|
params := map[string]string{
|
||||||
"pid": e.config["pid"], "type": req.PaymentType,
|
"pid": e.config["pid"], "type": req.PaymentType,
|
||||||
"out_trade_no": req.OrderID, "notify_url": req.NotifyURL,
|
"out_trade_no": req.OrderID, "notify_url": notifyURL,
|
||||||
"return_url": req.ReturnURL, "name": req.Subject,
|
"return_url": returnURL, "name": req.Subject,
|
||||||
"money": req.Amount, "clientip": req.ClientIP,
|
"money": req.Amount, "clientip": req.ClientIP,
|
||||||
}
|
}
|
||||||
if cid := e.resolveCID(req.PaymentType); cid != "" {
|
if cid := e.resolveCID(req.PaymentType); cid != "" {
|
||||||
@@ -89,6 +135,20 @@ func (e *EasyPay) CreatePayment(ctx context.Context, req payment.CreatePaymentRe
|
|||||||
return &payment.CreatePaymentResponse{TradeNo: resp.TradeNo, PayURL: resp.PayURL, QRCode: resp.QRCode}, nil
|
return &payment.CreatePaymentResponse{TradeNo: resp.TradeNo, PayURL: resp.PayURL, QRCode: resp.QRCode}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// resolveURLs returns (notifyURL, returnURL) preferring request values,
|
||||||
|
// falling back to instance config.
|
||||||
|
func (e *EasyPay) resolveURLs(req payment.CreatePaymentRequest) (string, string) {
|
||||||
|
notifyURL := req.NotifyURL
|
||||||
|
if notifyURL == "" {
|
||||||
|
notifyURL = e.config["notifyUrl"]
|
||||||
|
}
|
||||||
|
returnURL := req.ReturnURL
|
||||||
|
if returnURL == "" {
|
||||||
|
returnURL = e.config["returnUrl"]
|
||||||
|
}
|
||||||
|
return notifyURL, returnURL
|
||||||
|
}
|
||||||
|
|
||||||
func (e *EasyPay) QueryOrder(ctx context.Context, tradeNo string) (*payment.QueryOrderResponse, error) {
|
func (e *EasyPay) QueryOrder(ctx context.Context, tradeNo string) (*payment.QueryOrderResponse, error) {
|
||||||
params := map[string]string{
|
params := map[string]string{
|
||||||
"act": "order", "pid": e.config["pid"],
|
"act": "order", "pid": e.config["pid"],
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ const (
|
|||||||
TypeAlipayDirect PaymentType = "alipay_direct"
|
TypeAlipayDirect PaymentType = "alipay_direct"
|
||||||
TypeWxpayDirect PaymentType = "wxpay_direct"
|
TypeWxpayDirect PaymentType = "wxpay_direct"
|
||||||
TypeStripe PaymentType = "stripe"
|
TypeStripe PaymentType = "stripe"
|
||||||
|
TypeEasyPay PaymentType = "easypay"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Order status constants shared across payment and service layers.
|
// Order status constants shared across payment and service layers.
|
||||||
@@ -36,6 +37,8 @@ const (
|
|||||||
// For example, "alipay_direct" -> "alipay".
|
// For example, "alipay_direct" -> "alipay".
|
||||||
func GetBasePaymentType(t string) string {
|
func GetBasePaymentType(t string) string {
|
||||||
switch {
|
switch {
|
||||||
|
case t == "easypay":
|
||||||
|
return "easypay"
|
||||||
case len(t) >= 6 && t[:6] == "alipay":
|
case len(t) >= 6 && t[:6] == "alipay":
|
||||||
return "alipay"
|
return "alipay"
|
||||||
case len(t) >= 5 && t[:5] == "wxpay":
|
case len(t) >= 5 && t[:5] == "wxpay":
|
||||||
|
|||||||
@@ -339,7 +339,7 @@ func (s *PaymentService) invokeProvider(ctx context.Context, order *dbent.Paymen
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, infraerrors.ServiceUnavailable("PAYMENT_GATEWAY_ERROR", "payment method is temporarily unavailable")
|
return nil, infraerrors.ServiceUnavailable("PAYMENT_GATEWAY_ERROR", "payment method is temporarily unavailable")
|
||||||
}
|
}
|
||||||
_, err = s.entClient.PaymentOrder.UpdateOneID(order.ID).SetPaymentTradeNo(pr.TradeNo).SetNillablePayURL(psNilIfEmpty(pr.PayURL)).SetNillableQrCode(psNilIfEmpty(pr.QRCode)).SetNillableProviderInstanceID(psNilIfEmpty(sel.InstanceID)).Save(ctx)
|
_, err = s.entClient.PaymentOrder.UpdateOneID(order.ID).SetNillablePaymentTradeNo(psNilIfEmpty(pr.TradeNo)).SetNillablePayURL(psNilIfEmpty(pr.PayURL)).SetNillableQrCode(psNilIfEmpty(pr.QRCode)).SetNillableProviderInstanceID(psNilIfEmpty(sel.InstanceID)).Save(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("update order with payment details: %w", err)
|
return nil, fmt.Errorf("update order with payment details: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
-- 098_remove_easypay_from_enabled_payment_types.sql
|
||||||
|
--
|
||||||
|
-- Removes "easypay" from ENABLED_PAYMENT_TYPES setting.
|
||||||
|
-- "easypay" is a provider key, not a payment type. Valid payment types
|
||||||
|
-- are: alipay, wxpay, alipay_direct, wxpay_direct, stripe.
|
||||||
|
--
|
||||||
|
-- Idempotent: safe to run multiple times.
|
||||||
|
|
||||||
|
UPDATE settings
|
||||||
|
SET value = array_to_string(
|
||||||
|
array_remove(
|
||||||
|
string_to_array(value, ','),
|
||||||
|
'easypay'
|
||||||
|
), ','
|
||||||
|
)
|
||||||
|
WHERE key = 'ENABLED_PAYMENT_TYPES'
|
||||||
|
AND value LIKE '%easypay%';
|
||||||
@@ -67,6 +67,7 @@ function isStripe(type: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function iconBgClass(type: string): string {
|
function iconBgClass(type: string): string {
|
||||||
|
if (type === 'easypay') return 'bg-[#FF6B35]'
|
||||||
if (type.includes('alipay')) return 'bg-[#00AEEF]'
|
if (type.includes('alipay')) return 'bg-[#00AEEF]'
|
||||||
if (type.includes('wxpay')) return 'bg-[#07C160]'
|
if (type.includes('wxpay')) return 'bg-[#07C160]'
|
||||||
if (type === 'stripe') return 'bg-[#635bff]'
|
if (type === 'stripe') return 'bg-[#635bff]'
|
||||||
@@ -74,12 +75,14 @@ function iconBgClass(type: string): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function iconLabel(type: string): string {
|
function iconLabel(type: string): string {
|
||||||
|
if (type === 'easypay') return '易'
|
||||||
if (type.includes('alipay')) return '\u652f'
|
if (type.includes('alipay')) return '\u652f'
|
||||||
if (type.includes('wxpay')) return '\u5fae'
|
if (type.includes('wxpay')) return '\u5fae'
|
||||||
return type[0]?.toUpperCase() || '?'
|
return type[0]?.toUpperCase() || '?'
|
||||||
}
|
}
|
||||||
|
|
||||||
function methodSelectedClass(type: string): string {
|
function methodSelectedClass(type: string): string {
|
||||||
|
if (type === 'easypay') return 'border-[#FF6B35] bg-orange-50 text-gray-900 shadow-sm dark:bg-orange-950 dark:text-gray-100'
|
||||||
if (type.includes('alipay')) return 'border-[#00AEEF] bg-blue-50 text-gray-900 shadow-sm dark:bg-blue-950 dark:text-gray-100'
|
if (type.includes('alipay')) return 'border-[#00AEEF] bg-blue-50 text-gray-900 shadow-sm dark:bg-blue-950 dark:text-gray-100'
|
||||||
if (type.includes('wxpay')) return 'border-[#07C160] bg-green-50 text-gray-900 shadow-sm dark:bg-green-950 dark:text-gray-100'
|
if (type.includes('wxpay')) return 'border-[#07C160] bg-green-50 text-gray-900 shadow-sm dark:bg-green-950 dark:text-gray-100'
|
||||||
if (type === 'stripe') return 'border-[#635bff] bg-indigo-50 text-gray-900 shadow-sm dark:bg-indigo-950 dark:text-gray-100'
|
if (type === 'stripe') return 'border-[#635bff] bg-indigo-50 text-gray-900 shadow-sm dark:bg-indigo-950 dark:text-gray-100'
|
||||||
|
|||||||
@@ -4356,6 +4356,9 @@ export default {
|
|||||||
providerStripe: 'Stripe',
|
providerStripe: 'Stripe',
|
||||||
typeDisabled: 'type disabled',
|
typeDisabled: 'type disabled',
|
||||||
enableTypesFirst: 'Enable at least one payment type above first',
|
enableTypesFirst: 'Enable at least one payment type above first',
|
||||||
|
field_paymentMode: 'Payment Mode',
|
||||||
|
field_paymentMode_redirect: 'Redirect (Hosted Page)',
|
||||||
|
field_paymentMode_api: 'API Mode (QR Code)',
|
||||||
field_apiBase: 'API Base URL',
|
field_apiBase: 'API Base URL',
|
||||||
field_notifyUrl: 'Notify URL',
|
field_notifyUrl: 'Notify URL',
|
||||||
field_returnUrl: 'Return URL',
|
field_returnUrl: 'Return URL',
|
||||||
@@ -5041,6 +5044,7 @@ export default {
|
|||||||
actualPay: 'Actual Payment',
|
actualPay: 'Actual Payment',
|
||||||
createOrder: 'Confirm Payment',
|
createOrder: 'Confirm Payment',
|
||||||
methods: {
|
methods: {
|
||||||
|
easypay: 'EasyPay',
|
||||||
alipay: 'Alipay',
|
alipay: 'Alipay',
|
||||||
wxpay: 'WeChat Pay',
|
wxpay: 'WeChat Pay',
|
||||||
stripe: 'Stripe',
|
stripe: 'Stripe',
|
||||||
|
|||||||
@@ -4507,8 +4507,8 @@ export default {
|
|||||||
description: '配置支付系统选项',
|
description: '配置支付系统选项',
|
||||||
enabled: '启用支付',
|
enabled: '启用支付',
|
||||||
enabledHint: '启用或禁用支付系统',
|
enabledHint: '启用或禁用支付系统',
|
||||||
enabledPaymentTypes: '启用的支付方式',
|
enabledPaymentTypes: '服务商',
|
||||||
enabledPaymentTypesHint: '选择要显示的支付方式',
|
enabledPaymentTypesHint: '选择要启用的服务商',
|
||||||
minAmount: '最低金额',
|
minAmount: '最低金额',
|
||||||
maxAmount: '最高金额',
|
maxAmount: '最高金额',
|
||||||
dailyLimit: '每日限额',
|
dailyLimit: '每日限额',
|
||||||
@@ -4528,7 +4528,10 @@ export default {
|
|||||||
providerWxpay: '微信官方',
|
providerWxpay: '微信官方',
|
||||||
providerStripe: 'Stripe',
|
providerStripe: 'Stripe',
|
||||||
typeDisabled: '类型已禁用',
|
typeDisabled: '类型已禁用',
|
||||||
enableTypesFirst: '请先在上方启用至少一种支付方式',
|
enableTypesFirst: '请先在上方启用至少一种服务商',
|
||||||
|
field_paymentMode: '支付模式',
|
||||||
|
field_paymentMode_redirect: '跳转支付(托管页面)',
|
||||||
|
field_paymentMode_api: 'API 模式(二维码)',
|
||||||
field_apiBase: 'API 基础地址',
|
field_apiBase: 'API 基础地址',
|
||||||
field_notifyUrl: '异步通知地址',
|
field_notifyUrl: '异步通知地址',
|
||||||
field_returnUrl: '同步跳转地址',
|
field_returnUrl: '同步跳转地址',
|
||||||
@@ -5238,6 +5241,7 @@ export default {
|
|||||||
actualPay: '实付金额',
|
actualPay: '实付金额',
|
||||||
createOrder: '确认支付',
|
createOrder: '确认支付',
|
||||||
methods: {
|
methods: {
|
||||||
|
easypay: '易支付',
|
||||||
alipay: '支付宝',
|
alipay: '支付宝',
|
||||||
wxpay: '微信支付',
|
wxpay: '微信支付',
|
||||||
stripe: 'Stripe',
|
stripe: 'Stripe',
|
||||||
|
|||||||
@@ -1795,8 +1795,9 @@
|
|||||||
<div class="space-y-3">
|
<div class="space-y-3">
|
||||||
<div v-for="field in currentProviderFields" :key="field.key">
|
<div v-for="field in currentProviderFields" :key="field.key">
|
||||||
<label class="input-label">{{ field.label }} <span v-if="field.optional" class="text-xs text-gray-400">({{ t('common.optional') }})</span><span v-else class="text-red-500"> *</span></label>
|
<label class="input-label">{{ field.label }} <span v-if="field.optional" class="text-xs text-gray-400">({{ t('common.optional') }})</span><span v-else class="text-red-500"> *</span></label>
|
||||||
<textarea v-if="field.sensitive && field.key.toLowerCase().includes('key') && field.key !== 'pkey'" v-model="providerConfig[field.key]" rows="3" class="input font-mono text-xs" />
|
<Select v-if="field.type === 'select'" v-model="providerConfig[field.key]" :options="field.options || []" @change="onProviderConfigChange(field.key)" />
|
||||||
<input v-else :type="field.sensitive ? 'password' : 'text'" v-model="providerConfig[field.key]" class="input" />
|
<textarea v-else-if="field.sensitive && field.key.toLowerCase().includes('key') && field.key !== 'pkey'" v-model="providerConfig[field.key]" rows="3" class="input font-mono text-xs" :required="!field.optional && !editingProvider" />
|
||||||
|
<input v-else :type="field.sensitive ? 'password' : 'text'" v-model="providerConfig[field.key]" class="input" :required="!field.optional && !editingProvider" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="!currentProviderFields.length" class="text-sm text-gray-500 dark:text-gray-400">{{ t('admin.settings.payment.selectProviderKey') }}</div>
|
<div v-if="!currentProviderFields.length" class="text-sm text-gray-500 dark:text-gray-400">{{ t('admin.settings.payment.selectProviderKey') }}</div>
|
||||||
@@ -2897,14 +2898,14 @@ async function saveBetaPolicySettings() {
|
|||||||
// ==================== Provider Management ====================
|
// ==================== Provider Management ====================
|
||||||
|
|
||||||
const PROVIDER_SUPPORTED_TYPES: Record<string, string[]> = {
|
const PROVIDER_SUPPORTED_TYPES: Record<string, string[]> = {
|
||||||
easypay: ['alipay', 'wxpay'],
|
easypay: ['easypay', 'alipay', 'wxpay'],
|
||||||
alipay: ['alipay'],
|
alipay: ['alipay'],
|
||||||
wxpay: ['wxpay'],
|
wxpay: ['wxpay'],
|
||||||
stripe: ['stripe'],
|
stripe: ['stripe'],
|
||||||
}
|
}
|
||||||
|
|
||||||
const allPaymentTypes = computed(() => [
|
const allPaymentTypes = computed(() => [
|
||||||
{ value: 'easypay', label: t('admin.settings.payment.providerEasypay') },
|
{ value: 'easypay', label: t('payment.methods.easypay') },
|
||||||
{ value: 'alipay', label: t('payment.methods.alipay') },
|
{ value: 'alipay', label: t('payment.methods.alipay') },
|
||||||
{ value: 'wxpay', label: t('payment.methods.wxpay') },
|
{ value: 'wxpay', label: t('payment.methods.wxpay') },
|
||||||
{ value: 'stripe', label: t('payment.methods.stripe') },
|
{ value: 'stripe', label: t('payment.methods.stripe') },
|
||||||
@@ -2945,10 +2946,16 @@ interface ConfigFieldDef {
|
|||||||
label: string
|
label: string
|
||||||
sensitive: boolean
|
sensitive: boolean
|
||||||
optional?: boolean
|
optional?: boolean
|
||||||
|
type?: 'text' | 'select'
|
||||||
|
options?: { value: string; label: string }[]
|
||||||
}
|
}
|
||||||
|
|
||||||
const PROVIDER_CONFIG_FIELDS: Record<string, ConfigFieldDef[]> = {
|
const PROVIDER_CONFIG_FIELDS: Record<string, ConfigFieldDef[]> = {
|
||||||
easypay: [
|
easypay: [
|
||||||
|
{ key: 'paymentMode', label: '', sensitive: false, type: 'select', options: [
|
||||||
|
{ value: 'redirect', label: '' },
|
||||||
|
{ value: 'api', label: '' },
|
||||||
|
]},
|
||||||
{ key: 'pid', label: 'PID', sensitive: false },
|
{ key: 'pid', label: 'PID', sensitive: false },
|
||||||
{ key: 'pkey', label: 'PKey', sensitive: true },
|
{ key: 'pkey', label: 'PKey', sensitive: true },
|
||||||
{ key: 'apiBase', label: '', sensitive: false },
|
{ key: 'apiBase', label: '', sensitive: false },
|
||||||
@@ -2984,6 +2991,10 @@ const currentProviderFields = computed(() => {
|
|||||||
return fields.map(f => ({
|
return fields.map(f => ({
|
||||||
...f,
|
...f,
|
||||||
label: f.label || t(`admin.settings.payment.field_${f.key}`),
|
label: f.label || t(`admin.settings.payment.field_${f.key}`),
|
||||||
|
options: f.options?.map(o => ({
|
||||||
|
...o,
|
||||||
|
label: o.label || t(`admin.settings.payment.field_${f.key}_${o.value}`),
|
||||||
|
})),
|
||||||
}))
|
}))
|
||||||
})
|
})
|
||||||
const providerKeyOptions = computed(() => [
|
const providerKeyOptions = computed(() => [
|
||||||
@@ -2997,8 +3008,11 @@ const providerKeyOptions = computed(() => [
|
|||||||
const enabledProviderKeyOptions = computed(() => {
|
const enabledProviderKeyOptions = computed(() => {
|
||||||
const enabled = form.payment_enabled_types.split(',').map(s => s.trim()).filter(Boolean)
|
const enabled = form.payment_enabled_types.split(',').map(s => s.trim()).filter(Boolean)
|
||||||
return providerKeyOptions.value.filter(opt => {
|
return providerKeyOptions.value.filter(opt => {
|
||||||
// easypay is available if easypay OR alipay OR wxpay is enabled
|
// easypay provider is available if easypay, alipay, or wxpay payment type is enabled
|
||||||
if (opt.value === 'easypay') return enabled.includes('easypay') || enabled.includes('alipay') || enabled.includes('wxpay')
|
if (opt.value === 'easypay') return enabled.includes('easypay') || enabled.includes('alipay') || enabled.includes('wxpay')
|
||||||
|
// alipay/wxpay direct providers match their payment type
|
||||||
|
if (opt.value === 'alipay') return enabled.includes('alipay')
|
||||||
|
if (opt.value === 'wxpay') return enabled.includes('wxpay')
|
||||||
return enabled.includes(opt.value)
|
return enabled.includes(opt.value)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -3007,6 +3021,8 @@ const enabledProviderKeyOptions = computed(() => {
|
|||||||
function isProviderEnabled(providerKey: string): boolean {
|
function isProviderEnabled(providerKey: string): boolean {
|
||||||
const enabled = form.payment_enabled_types.split(',').map(s => s.trim()).filter(Boolean)
|
const enabled = form.payment_enabled_types.split(',').map(s => s.trim()).filter(Boolean)
|
||||||
if (providerKey === 'easypay') return enabled.includes('easypay') || enabled.includes('alipay') || enabled.includes('wxpay')
|
if (providerKey === 'easypay') return enabled.includes('easypay') || enabled.includes('alipay') || enabled.includes('wxpay')
|
||||||
|
if (providerKey === 'alipay') return enabled.includes('alipay')
|
||||||
|
if (providerKey === 'wxpay') return enabled.includes('wxpay')
|
||||||
return enabled.includes(providerKey)
|
return enabled.includes(providerKey)
|
||||||
}
|
}
|
||||||
const loadBalanceOptions = computed(() => [
|
const loadBalanceOptions = computed(() => [
|
||||||
@@ -3038,6 +3054,22 @@ function toggleProviderSupportedType(type: string) {
|
|||||||
function onProviderKeyChange() {
|
function onProviderKeyChange() {
|
||||||
// Auto-select all supported types for the new provider key
|
// Auto-select all supported types for the new provider key
|
||||||
providerForm.supported_types = (PROVIDER_SUPPORTED_TYPES[providerForm.provider_key] || []).join(',')
|
providerForm.supported_types = (PROVIDER_SUPPORTED_TYPES[providerForm.provider_key] || []).join(',')
|
||||||
|
// For easypay, set default payment mode and update supported types accordingly
|
||||||
|
if (providerForm.provider_key === 'easypay' && !providerConfig['paymentMode']) {
|
||||||
|
providerConfig['paymentMode'] = 'redirect'
|
||||||
|
providerForm.supported_types = 'easypay'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onProviderConfigChange(key: string) {
|
||||||
|
// When easypay paymentMode changes, auto-update supported types
|
||||||
|
if (key === 'paymentMode' && providerForm.provider_key === 'easypay') {
|
||||||
|
if (providerConfig['paymentMode'] === 'redirect') {
|
||||||
|
providerForm.supported_types = 'easypay'
|
||||||
|
} else {
|
||||||
|
providerForm.supported_types = 'alipay,wxpay'
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function providerKeyLabel(key: string): string {
|
function providerKeyLabel(key: string): string {
|
||||||
@@ -3052,10 +3084,16 @@ async function loadProviders() {
|
|||||||
function resetProviderForm() {
|
function resetProviderForm() {
|
||||||
const defaultKey = enabledProviderKeyOptions.value[0]?.value || 'easypay'
|
const defaultKey = enabledProviderKeyOptions.value[0]?.value || 'easypay'
|
||||||
providerForm.name = ''; providerForm.provider_key = defaultKey
|
providerForm.name = ''; providerForm.provider_key = defaultKey
|
||||||
providerForm.supported_types = (PROVIDER_SUPPORTED_TYPES[defaultKey] || []).join(',')
|
|
||||||
providerForm.enabled = true; providerForm.refund_enabled = false
|
providerForm.enabled = true; providerForm.refund_enabled = false
|
||||||
// Clear all config keys
|
// Clear all config keys
|
||||||
Object.keys(providerConfig).forEach(k => delete providerConfig[k])
|
Object.keys(providerConfig).forEach(k => delete providerConfig[k])
|
||||||
|
// Set defaults based on provider key
|
||||||
|
if (defaultKey === 'easypay') {
|
||||||
|
providerConfig['paymentMode'] = 'redirect'
|
||||||
|
providerForm.supported_types = 'easypay'
|
||||||
|
} else {
|
||||||
|
providerForm.supported_types = (PROVIDER_SUPPORTED_TYPES[defaultKey] || []).join(',')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
function openCreateProvider() { editingProvider.value = null; resetProviderForm(); showProviderDialog.value = true }
|
function openCreateProvider() { editingProvider.value = null; resetProviderForm(); showProviderDialog.value = true }
|
||||||
function openEditProvider(provider: ProviderInstance) {
|
function openEditProvider(provider: ProviderInstance) {
|
||||||
|
|||||||
Reference in New Issue
Block a user