|
@@ -12,6 +12,7 @@ use App\Models\RechargeChannel;
|
|
|
use App\Models\RechargeChannelGroup;
|
|
use App\Models\RechargeChannelGroup;
|
|
|
use App\Services\OperationAuditService;
|
|
use App\Services\OperationAuditService;
|
|
|
use App\Services\PaymentChannelLinkService;
|
|
use App\Services\PaymentChannelLinkService;
|
|
|
|
|
+use App\Services\Payment\PaymentProviderCatalog;
|
|
|
use Illuminate\Database\Eloquent\Builder;
|
|
use Illuminate\Database\Eloquent\Builder;
|
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\DB;
|
|
|
use Illuminate\Validation\Rule;
|
|
use Illuminate\Validation\Rule;
|
|
@@ -31,52 +32,18 @@ class PaymentConfiguration extends Controller
|
|
|
public function options()
|
|
public function options()
|
|
|
{
|
|
{
|
|
|
return $this->run(function () {
|
|
return $this->run(function () {
|
|
|
- $groups = RechargeChannelGroup::query()->orderBy('id')->get()->map(fn ($group) => [
|
|
|
|
|
- 'id' => (int)$group->id,
|
|
|
|
|
- 'name' => (string)$group->name,
|
|
|
|
|
- 'recharge_type' => (array)$group->recharge_type,
|
|
|
|
|
- 'withdraw_type' => (array)$group->withdraw_type,
|
|
|
|
|
- 'activity_type' => (array)$group->activity_type,
|
|
|
|
|
|
|
+ $filters = request()->validate([
|
|
|
|
|
+ 'kind' => ['nullable', Rule::in(['deposit', 'withdraw', 'collection_scan', 'collection_third_party'])],
|
|
|
|
|
+ 'payment_company' => ['nullable', Rule::in(PaymentProviderCatalog::codes())],
|
|
|
|
|
+ 'provider_name' => ['nullable', Rule::in(PaymentProviderCatalog::codes())],
|
|
|
|
|
+ 'payment_method' => ['nullable', 'string', 'max:64'],
|
|
|
|
|
+ 'collection_method' => ['nullable', 'string', 'max:64'],
|
|
|
]);
|
|
]);
|
|
|
- $channels = RechargeChannel::query()->orderBy('data_type')->orderBy('sort')->orderBy('id')
|
|
|
|
|
- ->get()->map(function ($channel) use ($groups) {
|
|
|
|
|
- $groupField = match ((int)$channel->data_type) {
|
|
|
|
|
- 2 => 'withdraw_type',
|
|
|
|
|
- 3 => 'activity_type',
|
|
|
|
|
- default => 'recharge_type',
|
|
|
|
|
- };
|
|
|
|
|
- return [
|
|
|
|
|
- 'id' => (int)$channel->id,
|
|
|
|
|
- 'data_type' => (int)$channel->data_type,
|
|
|
|
|
- 'name' => (string)$channel->name,
|
|
|
|
|
- 'key' => (string)$channel->key,
|
|
|
|
|
- 'type' => (string)$channel->type,
|
|
|
|
|
- 'rate' => number_format((float)$channel->rate, 4, '.', ''),
|
|
|
|
|
- 'fee_rate' => bcmul((string)$channel->rate, '100', 4),
|
|
|
|
|
- 'min_amount' => $channel->min,
|
|
|
|
|
- 'max_amount' => $channel->max,
|
|
|
|
|
- 'fixed_amounts' => $channel->fixed ?: [],
|
|
|
|
|
- 'sort' => (int)$channel->sort,
|
|
|
|
|
- 'status' => (int)$channel->status,
|
|
|
|
|
- 'available_group_ids' => $groups->filter(fn ($group) => in_array(
|
|
|
|
|
- (string)$channel->type,
|
|
|
|
|
- (array)$group[$groupField],
|
|
|
|
|
- true
|
|
|
|
|
- ))->pluck('id')->values(),
|
|
|
|
|
- ];
|
|
|
|
|
- });
|
|
|
|
|
- return [
|
|
|
|
|
- 'recharge_channels' => $channels,
|
|
|
|
|
- 'channel_groups' => $groups,
|
|
|
|
|
- 'levels' => $groups->map(fn ($group) => [
|
|
|
|
|
- 'value' => $group['id'],
|
|
|
|
|
- 'label' => $group['name'],
|
|
|
|
|
- ])->values(),
|
|
|
|
|
- 'payment_companies' => PaymentGateway::query()->distinct()->orderBy('payment_company')->pluck('payment_company')->filter()->values(),
|
|
|
|
|
- 'payment_methods' => PaymentGateway::query()->distinct()->orderBy('payment_method')->pluck('payment_method')->filter()->values(),
|
|
|
|
|
- 'collection_providers' => PaymentCollectionChannel::query()->distinct()->orderBy('provider_name')->pluck('provider_name')->filter()->values(),
|
|
|
|
|
- 'collection_methods' => PaymentCollectionChannel::query()->distinct()->orderBy('collection_method')->pluck('collection_method')->filter()->values(),
|
|
|
|
|
- ];
|
|
|
|
|
|
|
+ return (new PaymentProviderCatalog())->options(
|
|
|
|
|
+ RechargeChannel::query()->orderBy('data_type')->orderBy('sort')->orderBy('id')->get()->toArray(),
|
|
|
|
|
+ RechargeChannelGroup::query()->orderBy('id')->get()->toArray(),
|
|
|
|
|
+ $filters
|
|
|
|
|
+ );
|
|
|
});
|
|
});
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -166,36 +133,22 @@ class PaymentConfiguration extends Controller
|
|
|
'recharge_channel_group_ids.*' => ['integer'],
|
|
'recharge_channel_group_ids.*' => ['integer'],
|
|
|
'kind' => ['required', Rule::in(['deposit', 'withdraw'])],
|
|
'kind' => ['required', Rule::in(['deposit', 'withdraw'])],
|
|
|
'currency' => ['nullable', 'string', 'max:16'],
|
|
'currency' => ['nullable', 'string', 'max:16'],
|
|
|
- 'payment_company' => ['required', 'string', 'max:100'],
|
|
|
|
|
|
|
+ 'payment_company' => ['required', Rule::in(PaymentProviderCatalog::codes())],
|
|
|
'merchant_no' => ['required', 'string', 'max:128'],
|
|
'merchant_no' => ['required', 'string', 'max:128'],
|
|
|
- 'payment_method' => ['nullable', 'string', 'max:64'],
|
|
|
|
|
|
|
+ 'payment_method' => ['required', 'string', 'max:64'],
|
|
|
'channel_code' => ['nullable', 'string', 'max:64'],
|
|
'channel_code' => ['nullable', 'string', 'max:64'],
|
|
|
'channel_name' => ['nullable', 'string', 'max:100'],
|
|
'channel_name' => ['nullable', 'string', 'max:100'],
|
|
|
'withdrawal_secret' => ['nullable', 'string', 'max:10000'],
|
|
'withdrawal_secret' => ['nullable', 'string', 'max:10000'],
|
|
|
|
|
+ 'deposit_secret' => ['nullable', 'string', 'max:10000'],
|
|
|
'status' => ['nullable', Rule::in([0, 1])],
|
|
'status' => ['nullable', Rule::in([0, 1])],
|
|
|
]);
|
|
]);
|
|
|
$expectedDataType = $params['kind'] === 'withdraw' ? 2 : 1;
|
|
$expectedDataType = $params['kind'] === 'withdraw' ? 2 : 1;
|
|
|
- [, $params['recharge_channel_group_ids']] = $this->channelLinks->validate(
|
|
|
|
|
|
|
+ [$channel, $params['recharge_channel_group_ids']] = $this->channelLinks->validate(
|
|
|
(int)$params['recharge_channel_id'],
|
|
(int)$params['recharge_channel_id'],
|
|
|
$params['recharge_channel_group_ids'],
|
|
$params['recharge_channel_group_ids'],
|
|
|
$expectedDataType
|
|
$expectedDataType
|
|
|
);
|
|
);
|
|
|
- $existing = !empty($params['id']) ? PaymentGateway::query()->findOrFail($params['id']) : null;
|
|
|
|
|
- if ($existing && $existing->kind !== $params['kind']) {
|
|
|
|
|
- throw ValidationException::withMessages(['kind' => '网关类型创建后不能修改']);
|
|
|
|
|
- }
|
|
|
|
|
- if ($params['kind'] === 'deposit' && empty($params['payment_method'])) {
|
|
|
|
|
- throw ValidationException::withMessages(['payment_method' => '存款网关必须填写支付方式']);
|
|
|
|
|
- }
|
|
|
|
|
- $hasSavedSecret = $existing ? $existing->has_withdrawal_secret : false;
|
|
|
|
|
- if ($params['kind'] === 'withdraw' && empty($params['withdrawal_secret']) && !$hasSavedSecret) {
|
|
|
|
|
- throw ValidationException::withMessages(['withdrawal_secret' => '出款网关必须填写出款密钥']);
|
|
|
|
|
- }
|
|
|
|
|
- if ($params['kind'] === 'deposit') {
|
|
|
|
|
- $params['withdrawal_secret'] = null;
|
|
|
|
|
- } elseif (empty($params['withdrawal_secret'])) {
|
|
|
|
|
- unset($params['withdrawal_secret']);
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ PaymentProviderCatalog::validateSelection($channel->toArray(), $params['payment_company'], $params['payment_method'], $expectedDataType);
|
|
|
return $this->saveModel(PaymentGateway::class, $params, 'payment_gateway');
|
|
return $this->saveModel(PaymentGateway::class, $params, 'payment_gateway');
|
|
|
});
|
|
});
|
|
|
}
|
|
}
|
|
@@ -242,18 +195,19 @@ class PaymentConfiguration extends Controller
|
|
|
'recharge_channel_group_ids.*' => ['integer'],
|
|
'recharge_channel_group_ids.*' => ['integer'],
|
|
|
'kind' => ['required', Rule::in(['collection_scan', 'collection_third_party'])],
|
|
'kind' => ['required', Rule::in(['collection_scan', 'collection_third_party'])],
|
|
|
'currency' => ['nullable', 'string', 'max:16'],
|
|
'currency' => ['nullable', 'string', 'max:16'],
|
|
|
- 'provider_name' => ['required', 'string', 'max:100'],
|
|
|
|
|
|
|
+ 'provider_name' => ['required', Rule::in(PaymentProviderCatalog::codes())],
|
|
|
'group_name' => ['required', 'string', 'max:100'],
|
|
'group_name' => ['required', 'string', 'max:100'],
|
|
|
'collection_method' => ['required', 'string', 'max:64'],
|
|
'collection_method' => ['required', 'string', 'max:64'],
|
|
|
'channel_code' => ['required', 'string', 'max:64'],
|
|
'channel_code' => ['required', 'string', 'max:64'],
|
|
|
'channel_name' => ['required', 'string', 'max:100'],
|
|
'channel_name' => ['required', 'string', 'max:100'],
|
|
|
'status' => ['nullable', Rule::in([0, 1])],
|
|
'status' => ['nullable', Rule::in([0, 1])],
|
|
|
]);
|
|
]);
|
|
|
- [, $params['recharge_channel_group_ids']] = $this->channelLinks->validate(
|
|
|
|
|
|
|
+ [$channel, $params['recharge_channel_group_ids']] = $this->channelLinks->validate(
|
|
|
(int)$params['recharge_channel_id'],
|
|
(int)$params['recharge_channel_id'],
|
|
|
$params['recharge_channel_group_ids'],
|
|
$params['recharge_channel_group_ids'],
|
|
|
1
|
|
1
|
|
|
);
|
|
);
|
|
|
|
|
+ PaymentProviderCatalog::validateSelection($channel->toArray(), $params['provider_name'], $params['collection_method'], 1);
|
|
|
return $this->saveModel(PaymentCollectionChannel::class, $params, 'payment_collection_channel');
|
|
return $this->saveModel(PaymentCollectionChannel::class, $params, 'payment_collection_channel');
|
|
|
});
|
|
});
|
|
|
}
|
|
}
|
|
@@ -297,6 +251,7 @@ class PaymentConfiguration extends Controller
|
|
|
unset($params['id']);
|
|
unset($params['id']);
|
|
|
$params += OperationAuditService::actor();
|
|
$params += OperationAuditService::actor();
|
|
|
$model = $id ? $modelClass::query()->lockForUpdate()->findOrFail($id) : new $modelClass();
|
|
$model = $id ? $modelClass::query()->lockForUpdate()->findOrFail($id) : new $modelClass();
|
|
|
|
|
+ if ($model instanceof PaymentGateway) $params = $this->gatewayCredentials($model, $params);
|
|
|
$before = $model->exists ? $model->replicate()->setRawAttributes($model->getRawOriginal()) : [];
|
|
$before = $model->exists ? $model->replicate()->setRawAttributes($model->getRawOriginal()) : [];
|
|
|
$model->fill($params);
|
|
$model->fill($params);
|
|
|
$model->save();
|
|
$model->save();
|
|
@@ -319,6 +274,27 @@ class PaymentConfiguration extends Controller
|
|
|
});
|
|
});
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ private function gatewayCredentials(PaymentGateway $model, array $params): array
|
|
|
|
|
+ {
|
|
|
|
|
+ if ($model->exists && $model->kind !== $params['kind']) {
|
|
|
|
|
+ throw ValidationException::withMessages(['kind' => '网关类型创建后不能修改']);
|
|
|
|
|
+ }
|
|
|
|
|
+ $secretField = $params['kind'] === 'deposit' ? 'deposit_secret' : 'withdrawal_secret';
|
|
|
|
|
+ $otherField = $params['kind'] === 'deposit' ? 'withdrawal_secret' : 'deposit_secret';
|
|
|
|
|
+ $hasSecret = !empty($model->getRawOriginal($secretField));
|
|
|
|
|
+ $identityChanged = $model->exists && ($model->payment_company !== $params['payment_company']
|
|
|
|
|
+ || $model->merchant_no !== $params['merchant_no']);
|
|
|
|
|
+ $newSecret = $params[$secretField] ?? null;
|
|
|
|
|
+ if ($newSecret === null || $newSecret === '') {
|
|
|
|
|
+ if (!$hasSecret || $identityChanged) {
|
|
|
|
|
+ throw ValidationException::withMessages([$secretField => '新增商户或切换公司/商户号时必须填写签名密钥']);
|
|
|
|
|
+ }
|
|
|
|
|
+ unset($params[$secretField]);
|
|
|
|
|
+ }
|
|
|
|
|
+ $params[$otherField] = null;
|
|
|
|
|
+ return $params;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
private function deleteModel(string $modelClass, string $resourceType)
|
|
private function deleteModel(string $modelClass, string $resourceType)
|
|
|
{
|
|
{
|
|
|
return $this->run(function () use ($modelClass, $resourceType) {
|
|
return $this->run(function () use ($modelClass, $resourceType) {
|