Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -214,3 +214,5 @@
- BO/FO : Accessibility improvements for EAA / WCAG 2.1 AA compliance
- Added configurable payment description and order reference on payment page
- API update to V1.50: added WERO and GIFTCARD payment methods, removed deprecated GIROPAY/PAYDIREKT/SOFORT
- BO : Fixed issue when a freshly installed module logged an account error before any API credentials were entered
- BO : Fixed issue when the "Could not reach your Saferpay account" warning kept showing after payment methods had loaded successfully
45 changes: 40 additions & 5 deletions controllers/admin/AdminSaferPayOfficialSettingsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,9 @@ private function collectSettingsData()
/** @var SaferPayConfiguration $configuration */
$configuration = $this->module->getService(SaferPayConfiguration::class);

// Resolved before the payload is built because it sets $paymentMethodsFetchFailed.
$paymentMethodsData = $this->getPaymentMethodsData();

$data = [
// Environment
'testMode' => (bool) $configuration->get(SaferPayConfig::TEST_MODE),
Expand Down Expand Up @@ -615,7 +618,7 @@ private function collectSettingsData()
'orderStates' => $this->getOrderStates(),
'countries' => $this->getCountries(),
'currencies' => $this->getCurrencies(),
'paymentMethods' => $this->getPaymentMethodsData(),
'paymentMethods' => $paymentMethodsData,
'paymentMethodsFetchFailed' => $this->paymentMethodsFetchFailed,

// Endpoints
Expand Down Expand Up @@ -698,22 +701,29 @@ private function getPaymentMethodsData()
/** @var SaferPayPaymentRepository $paymentRepository */
$paymentRepository = $this->module->getService(SaferPayPaymentRepository::class);

// A fresh install has no credentials yet, so calling the account would fail and
// write a misleading error to the merchant's log. Skip the account entirely until
// the credentials needed to build the request are present.
$hasCredentials = $this->hasApiCredentials();

try {
// Re-read the account and reconcile the stored list when the Payment Methods
// settings open, so methods added/removed on the Saferpay account are reflected
// (and persisted for the front office) without requiring a Save click. Enabled
// flags are preserved by the refresh; newly added methods default to disabled.
/** @var SaferPayRefreshPaymentsService $refreshPaymentsService */
$refreshPaymentsService = $this->module->getService(SaferPayRefreshPaymentsService::class);
$refreshPaymentsService->refreshPayments();
if ($hasCredentials) {
/** @var SaferPayRefreshPaymentsService $refreshPaymentsService */
$refreshPaymentsService = $this->module->getService(SaferPayRefreshPaymentsService::class);
$refreshPaymentsService->refreshPayments();
}

// The refresh persists the account's methods, so read them back from storage
// instead of calling the API a second time.
$paymentMethods = array_column($paymentRepository->getAllPaymentMethodsNames(), 'name');

// refreshPayments() is a no-op when nothing is active yet (e.g. a fresh setup),
// so fall back to the live account list to still surface newly available methods.
if (empty($paymentMethods)) {
if (empty($paymentMethods) && $hasCredentials) {
/** @var SaferPayObtainPaymentMethods $obtainMethods */
$obtainMethods = $this->module->getService(SaferPayObtainPaymentMethods::class);
$paymentMethods = $obtainMethods->obtainPaymentMethodsNamesAsArray();
Expand Down Expand Up @@ -768,6 +778,31 @@ private function getPaymentMethodsData()
return $result;
}

/**
* Whether every credential the payment methods request is built from is configured
* for the active environment.
*
* @return bool
*/
private function hasApiCredentials()
{
$suffix = SaferPayConfig::getConfigSuffix();
$required = [
SaferPayConfig::USERNAME,
SaferPayConfig::PASSWORD,
SaferPayConfig::CUSTOMER_ID,
SaferPayConfig::TERMINAL_ID,
];

foreach ($required as $key) {
if (!Configuration::get($key . $suffix)) {
return false;
}
}

return true;
}

/**
* Get JSON input from request body
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,14 @@ export function PaymentMethods() {
// The account check runs while the page bootstraps, so a failure arrives via
// the initial settings data rather than a refresh response.
useEffect(() => {
if (settings.paymentMethodsFetchFailed && !fetchFailedToastShown) {
// Clearing the latch keeps a later, genuine failure from being swallowed.
if (!settings.paymentMethodsFetchFailed) {
fetchFailedToastShown = false

return
}

if (!fetchFailedToastShown) {
fetchFailedToastShown = true
toast({ title: t('paymentMethodsUnreachable'), variant: 'warning' })
}
Expand Down
13 changes: 11 additions & 2 deletions views/js/admin/settings-app/src/context/settings-context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,17 @@ export function SettingsProvider({ children }: { children: React.ReactNode }) {
if (result.success && Array.isArray(result.data?.paymentMethods)) {
setPaymentMethods(result.data.paymentMethods as PaymentMethodData[])
}
if (result.data?.paymentMethodsFetchFailed === true) {
toast({ title: t('paymentMethodsUnreachable'), variant: 'warning' })

// Mirror the refreshed outcome onto the bootstrap flag, otherwise a failure from
// page load keeps warning after a later refresh has already succeeded. Only a
// response that actually reports the flag may clear it.
if (result.data && 'paymentMethodsFetchFailed' in result.data) {
const fetchFailed = result.data.paymentMethodsFetchFailed === true
setSettings((prev) => ({ ...prev, paymentMethodsFetchFailed: fetchFailed }))

if (fetchFailed) {
toast({ title: t('paymentMethodsUnreachable'), variant: 'warning' })
}
}
} catch {
toast({ title: t('errorRefreshingPaymentMethods'), variant: 'destructive' })
Expand Down
Loading