Commit 8fa168e244b for woocommerce
commit 8fa168e244ba34c4dc0e1546508299dd4c612e74
Author: Vlad Olaru <vlad.olaru@automattic.com>
Date: Tue Aug 4 16:48:28 2026 +0300
[Payments NOX] Add Helcim as a recommended payment partner (#66960)
* feat(payments): add Helcim recommendation
Context:
Helcim is an official payment partner for merchants in the United States and Canada.
Problem:
The extension was absent from payment recommendations, so eligible merchants could not discover it from WooCommerce settings.
Solution:
Register Helcim as the last non-preferred option in both countries, supply its Marketplace and WordPress.org metadata, and cover the ordering and provider details with focused tests. A shared payment icon remains in place until the final Helcim asset is supplied.
Refs WOOPRD-3590
* chore(changelog): document Helcim recommendation
Context:
WooCommerce package changes require a release note for changelog generation.
Problem:
The new Helcim recommendation was not represented in the package changelog.
Solution:
Add a minor feature entry for Helcim availability in the United States and Canada.
Refs WOOPRD-3590
* fix(payments): place Helcim after other PSPs
Context: Helcim is the final payment service provider recommendation for Canada and the United States.
Problem: Appending it after the BNPL entries gave it the wrong priority, while the regression test encoded that placement as the expected behavior.
Solution: Position Helcim after the final country-specific PSP and assert it remains immediately before express checkout suggestions.
Refs WOOPRD-3590
* fix(payments): update Helcim recommendation copy
Context: The Helcim recommendation needs to communicate its supported checkout method and pricing model.
Problem: The initial generic description omitted Google Pay, zero monthly fees, and Helcim's interchange-plus savings claim.
Solution: Replace the translatable description with the approved partner copy while leaving recommendation behavior unchanged.
Refs WOOPRD-3590
* feat(payments): add Helcim resource links
Context: Payment recommendations expose typed links for merchant research and support.
Problem: The Helcim recommendation included only its Marketplace page, leaving pricing, terms, documentation, and support unavailable from the provider metadata.
Solution: Add the four partner-provided resources and cover the complete ordered link set in the existing Helcim details test.
Refs WOOPRD-3590
* test(payments): reduce Helcim test coupling
Context: Helcim's focused coverage protects its supported markets, PSP placement, install metadata, resource links, and presentation details.
Problem: The tests also encoded current neighboring providers, resource-link array order, and the temporary fallback icon. Those details can change without altering the recommendation contract.
Solution: Assert that Helcim is the final PSP, compare its exact typed links canonically, and require a non-empty icon while retaining the stable behavioral checks.
Refs WOOPRD-3590
* Add official Helcim icon
Use the dedicated Helcim icon for the payments recommendation instead
of the shared default-payments fallback.
* Fix Helcim icon showing a transparent sliver in non-square containers
The root <svg> had no preserveAspectRatio, so it defaulted to
xMidYMid meet: whenever the rendering box wasn't exactly 1:1 (e.g.
GitHub's file preview, off by ~1px), the square artwork was
letterboxed instead of filling the box, leaving a visible gap next
to the gradient background. Setting xMidYMid slice crops to fill
instead.
* Shrink Helcim icon's embedded background to its actual display size
The pattern embedded the background PNG at 1024x1024 even though the
icon's viewBox is 160x160, so the raster data was 6.4x larger than
ever rendered. Swapped in a 160x160 version of the same gradient;
visually identical since it's a smooth low-frequency blur with no
fine detail to lose. File size drops from 59.3KB to 16.7KB.
* test: bump payment provider suggestion counts for Helcim addition
This PR adds Helcim to the US/CA payment provider suggestion list,
which shifted the real suggestion count returned for US merchants by
+1. PaymentsRestControllerIntegrationTest hardcodes the expected
provider/suggestion counts per scenario, so six assertions (and their
matching inline comments) were left pointing at the pre-Helcim counts,
failing 4 CI jobs (PHP 8.5, PHP 8.5 HPOS:off, PHP 7.4 latest, PHP 7.4
pre-release) with "actual size N+1 matches expected size N".
Bump each affected assertCount() (and its comment) by one to match the
new suggestion count. No production behavior changed.
Refs WOOPRD-3590
* Fix PHPStan false positive on SafeGlobalFunctionProxy::wc_get_logger()
SafeGlobalFunctionProxy::wc_get_logger() is resolved through
__callStatic() with no @method PHPDoc, so PHPStan flags every call
site as an undefined static method — 48 occurrences already carried
in phpstan-baseline.neon. Adding a new caller would need its own
baseline entries, growing a file that's only supposed to shrink.
Add the missing @method annotation so PHPStan can resolve the
proxied call directly, and regenerate the baseline. No behavior
change; the baseline drops by 192 lines as all wc_get_logger() false
positives resolve at once.
Refs WOOPRD-3590
* Add Helcim gateway connected/test-mode detection
The Payments settings screen falls back to generic heuristics for
account-connected and test-mode state when a gateway doesn't expose
a dedicated API for either. Helcim exposes neither, so it always
reported account_connected=true regardless of whether an API token
was ever saved, and never reflected its actual sandbox/live setting
in test_mode.
Add a Helcim provider class, verified against the real
helcim-commerce-for-woocommerce plugin (its `environment`,
`sandbox_api_token`, and `live_api_token` gateway options), following
the same defensive shape as the existing Mollie and GoCardless
providers: try the gateway-specific check, log and fall back to the
parent class's generic heuristic on any failure.
Refs WOOPRD-3590
* Update plugins/woocommerce/src/Internal/Admin/Settings/PaymentsProviders/Helcim.php
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update plugins/woocommerce/src/Internal/Admin/Settings/PaymentsProviders/Helcim.php
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Fix Helcim connected/test-mode detection on pre-v5 installs
Pre-v5 Helcim installs register the same gateway id (helcimjs) but
never had an 'environment' option — that field, and the
sandbox_api_token/live_api_token split, only exist from v5 onward.
get_option( 'environment', 'sandbox' ) silently substituted 'sandbox'
for the missing key, so a live, connected pre-v5 merchant was read as
sandbox mode with no token, reporting test_mode=true and
account_connected=false instead of falling through to the generic
heuristic.
Drop the 'sandbox' default so a missing option surfaces as an empty
string, which the existing sandbox/live/null branch correctly treats
as "unknown" and defers to the parent class. Confirmed against the
real WC_Settings_API::get_option() that this is a no-op for v5 (its
own form field already defaults to 'sandbox') and fixes the pre-v5
case (verified account_connected/test_mode before and after).
Also fixes an inert `@internal` PHPDoc tag (wrapped in backticks, so
it read as inline code rather than a real annotation — the only such
occurrence in src/), and adds regression coverage for a missing
environment option and an unrecognized value.
Refs WOOPRD-3590
* Shorten Helcim suggestion title to match brand-name convention
Refs WOOPRD-3590
* Strengthen unrecognized-environment test to actually kill mutants
test_is_account_connected_falls_back_for_unrecognized_environment used
account_connected => false, which is what is_account_connected() also
returns if is_helcim_in_sandbox_mode()'s null branch were mutated to
true (looks up an absent sandbox_api_token) or false (looks up an
absent live_api_token) — all three paths converge on false, so the
assertion couldn't distinguish the correct null-fallback behavior from
either wrong branch.
Flip the fixture to account_connected => true: only the correct
null-and-defer-to-parent path reaches it, so a mutation to either true
or false now fails the test. Verified against real mutations of both
branches.
Refs WOOPRD-3590
---------
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
diff --git a/plugins/woocommerce/assets/images/onboarding/icons/helcim.svg b/plugins/woocommerce/assets/images/onboarding/icons/helcim.svg
new file mode 100644
index 00000000000..1e1322f0ffe
--- /dev/null
+++ b/plugins/woocommerce/assets/images/onboarding/icons/helcim.svg
@@ -0,0 +1 @@
+<svg fill="none" height="160" viewBox="0 0 160 160" width="160" preserveAspectRatio="xMidYMid slice" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><pattern id="a" height="1" patternContentUnits="objectBoundingBox" width="1"><image height="160" preserveAspectRatio="none" transform="scale(.00625)" width="160" xlink:href="data:image/png;base64,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"/></pattern><mask id="b" height="160" maskUnits="userSpaceOnUse" width="160" x="0" y="0"><path d="m160 0h-160v160h160z" fill="#fff"/></mask><g mask="url(#b)"><path d="m160 0h-160v160h160z" fill="url(#a)"/><path d="m91.0756 127.128c-8.9549-3.135-10.1028-12.904-8.2711-21.126.4396-2.772 1.27-6.1859 2.1573-10.4112.9362-4.3943 1.7096-7.8084 2.1574-10.2337.4296-2.1945.6882-4.4214.7733-6.6591 0-4.5633-1.5467-6.9971-4.6484-6.9971-2.1573 0-7.7419 1.8085-10.1272 12.5576 0 0-1.2455 7.445-5.2345 24.4475-5.2997 27.389-17.4377 18.245-27.8824 14.053 3.0528-.194 6.7488-4.478 8.7107-11.687 6.7162-24.6332 16.0944-75.6918 16.0944-75.6918l19.6927-3.3802-7.7256 34.7573c2.191-2.6867 4.9564-4.805 8.0757-6.1858 2.2496-.8642 4.6268-1.316 7.0255-1.3352 4.3716 0 7.8071 1.1577 10.4606 3.5915 2.662 2.6028 4.071 6.5323 4.071 11.7379-.158 4.1217-.734 8.2147-1.718 12.2111-.334 1.4451-.773 3.5831-1.384 6.5323-1.278 4.8591-4.2819 16.4869-.122 21.3969 5.324 6.279 13.644-4.225 16.819-10.36-4.258 23.602-21.7279 25.318-28.9244 22.782z" fill="#6949b0"/></g></svg>
\ No newline at end of file
diff --git a/plugins/woocommerce/changelog/wooprd-3590-new-payment-partner-helcim b/plugins/woocommerce/changelog/wooprd-3590-new-payment-partner-helcim
new file mode 100644
index 00000000000..ed70e3d8b48
--- /dev/null
+++ b/plugins/woocommerce/changelog/wooprd-3590-new-payment-partner-helcim
@@ -0,0 +1,4 @@
+Significance: minor
+Type: add
+
+Add Helcim to payment method recommendations in the United States and Canada.
diff --git a/plugins/woocommerce/phpstan-baseline.neon b/plugins/woocommerce/phpstan-baseline.neon
index df45d7db27f..247bbfa9d31 100644
--- a/plugins/woocommerce/phpstan-baseline.neon
+++ b/plugins/woocommerce/phpstan-baseline.neon
@@ -13110,12 +13110,6 @@ parameters:
count: 2
path: includes/class-wc-order.php
- -
- message: '#^Call to an undefined static method Automattic\\WooCommerce\\Internal\\Logging\\SafeGlobalFunctionProxy\:\:wc_get_logger\(\)\.$#'
- identifier: staticMethod.notFound
- count: 1
- path: includes/class-wc-payment-gateways.php
-
-
message: '#^Cannot call method set_current\(\) on class\-string\|object\.$#'
identifier: method.nonObject
@@ -59001,24 +58995,12 @@ parameters:
count: 1
path: src/Internal/Admin/Settings.php
- -
- message: '#^Call to an undefined static method Automattic\\WooCommerce\\Internal\\Logging\\SafeGlobalFunctionProxy\:\:wc_get_logger\(\)\.$#'
- identifier: staticMethod.notFound
- count: 1
- path: src/Internal/Admin/Settings/Payments.php
-
-
message: '#^Access to an undefined property WooCommerce\:\:\$payment_gateways\.$#'
identifier: property.notFound
count: 1
path: src/Internal/Admin/Settings/PaymentsController.php
- -
- message: '#^Call to an undefined static method Automattic\\WooCommerce\\Internal\\Logging\\SafeGlobalFunctionProxy\:\:wc_get_logger\(\)\.$#'
- identifier: staticMethod.notFound
- count: 2
- path: src/Internal/Admin/Settings/PaymentsController.php
-
-
message: '#^Method Automattic\\WooCommerce\\Internal\\Admin\\Settings\\PaymentsController\:\:add_menu\(\) has no return type specified\.$#'
identifier: missingType.return
@@ -59085,90 +59067,6 @@ parameters:
count: 1
path: src/Internal/Admin/Settings/PaymentsProviders.php
- -
- message: '#^Call to an undefined static method Automattic\\WooCommerce\\Internal\\Logging\\SafeGlobalFunctionProxy\:\:wc_get_logger\(\)\.$#'
- identifier: staticMethod.notFound
- count: 1
- path: src/Internal/Admin/Settings/PaymentsProviders/Affirm.php
-
- -
- message: '#^Call to an undefined static method Automattic\\WooCommerce\\Internal\\Logging\\SafeGlobalFunctionProxy\:\:wc_get_logger\(\)\.$#'
- identifier: staticMethod.notFound
- count: 2
- path: src/Internal/Admin/Settings/PaymentsProviders/AfterpayClearpay.php
-
- -
- message: '#^Call to an undefined static method Automattic\\WooCommerce\\Internal\\Logging\\SafeGlobalFunctionProxy\:\:wc_get_logger\(\)\.$#'
- identifier: staticMethod.notFound
- count: 2
- path: src/Internal/Admin/Settings/PaymentsProviders/Airwallex.php
-
- -
- message: '#^Call to an undefined static method Automattic\\WooCommerce\\Internal\\Logging\\SafeGlobalFunctionProxy\:\:wc_get_logger\(\)\.$#'
- identifier: staticMethod.notFound
- count: 2
- path: src/Internal/Admin/Settings/PaymentsProviders/AmazonPay.php
-
- -
- message: '#^Call to an undefined static method Automattic\\WooCommerce\\Internal\\Logging\\SafeGlobalFunctionProxy\:\:wc_get_logger\(\)\.$#'
- identifier: staticMethod.notFound
- count: 2
- path: src/Internal/Admin/Settings/PaymentsProviders/Antom.php
-
- -
- message: '#^Call to an undefined static method Automattic\\WooCommerce\\Internal\\Logging\\SafeGlobalFunctionProxy\:\:wc_get_logger\(\)\.$#'
- identifier: staticMethod.notFound
- count: 2
- path: src/Internal/Admin/Settings/PaymentsProviders/Eway.php
-
- -
- message: '#^Call to an undefined static method Automattic\\WooCommerce\\Internal\\Logging\\SafeGlobalFunctionProxy\:\:wc_get_logger\(\)\.$#'
- identifier: staticMethod.notFound
- count: 1
- path: src/Internal/Admin/Settings/PaymentsProviders/GoCardless.php
-
- -
- message: '#^Call to an undefined static method Automattic\\WooCommerce\\Internal\\Logging\\SafeGlobalFunctionProxy\:\:wc_get_logger\(\)\.$#'
- identifier: staticMethod.notFound
- count: 2
- path: src/Internal/Admin/Settings/PaymentsProviders/HelioPay.php
-
- -
- message: '#^Call to an undefined static method Automattic\\WooCommerce\\Internal\\Logging\\SafeGlobalFunctionProxy\:\:wc_get_logger\(\)\.$#'
- identifier: staticMethod.notFound
- count: 1
- path: src/Internal/Admin/Settings/PaymentsProviders/Klarna.php
-
- -
- message: '#^Call to an undefined static method Automattic\\WooCommerce\\Internal\\Logging\\SafeGlobalFunctionProxy\:\:wc_get_logger\(\)\.$#'
- identifier: staticMethod.notFound
- count: 1
- path: src/Internal/Admin/Settings/PaymentsProviders/KlarnaCheckout.php
-
- -
- message: '#^Call to an undefined static method Automattic\\WooCommerce\\Internal\\Logging\\SafeGlobalFunctionProxy\:\:wc_get_logger\(\)\.$#'
- identifier: staticMethod.notFound
- count: 2
- path: src/Internal/Admin/Settings/PaymentsProviders/MercadoPago.php
-
- -
- message: '#^Call to an undefined static method Automattic\\WooCommerce\\Internal\\Logging\\SafeGlobalFunctionProxy\:\:wc_get_logger\(\)\.$#'
- identifier: staticMethod.notFound
- count: 2
- path: src/Internal/Admin/Settings/PaymentsProviders/Mollie.php
-
- -
- message: '#^Call to an undefined static method Automattic\\WooCommerce\\Internal\\Logging\\SafeGlobalFunctionProxy\:\:wc_get_logger\(\)\.$#'
- identifier: staticMethod.notFound
- count: 1
- path: src/Internal/Admin/Settings/PaymentsProviders/Monei.php
-
- -
- message: '#^Call to an undefined static method Automattic\\WooCommerce\\Internal\\Logging\\SafeGlobalFunctionProxy\:\:wc_get_logger\(\)\.$#'
- identifier: staticMethod.notFound
- count: 2
- path: src/Internal/Admin/Settings/PaymentsProviders/NexiCheckout.php
-
-
message: '#^Access to constant SANDBOX on an unknown class WooCommerce\\PayPalCommerce\\Onboarding\\Environment\.$#'
identifier: class.notFound
@@ -59181,30 +59079,6 @@ parameters:
count: 1
path: src/Internal/Admin/Settings/PaymentsProviders/PayPal.php
- -
- message: '#^Call to an undefined static method Automattic\\WooCommerce\\Internal\\Logging\\SafeGlobalFunctionProxy\:\:wc_get_logger\(\)\.$#'
- identifier: staticMethod.notFound
- count: 2
- path: src/Internal/Admin/Settings/PaymentsProviders/PayPal.php
-
- -
- message: '#^Call to an undefined static method Automattic\\WooCommerce\\Internal\\Logging\\SafeGlobalFunctionProxy\:\:wc_get_logger\(\)\.$#'
- identifier: staticMethod.notFound
- count: 1
- path: src/Internal/Admin/Settings/PaymentsProviders/PayUIndia.php
-
- -
- message: '#^Call to an undefined static method Automattic\\WooCommerce\\Internal\\Logging\\SafeGlobalFunctionProxy\:\:wc_get_logger\(\)\.$#'
- identifier: staticMethod.notFound
- count: 1
- path: src/Internal/Admin/Settings/PaymentsProviders/Payfast.php
-
- -
- message: '#^Call to an undefined static method Automattic\\WooCommerce\\Internal\\Logging\\SafeGlobalFunctionProxy\:\:wc_get_logger\(\)\.$#'
- identifier: staticMethod.notFound
- count: 16
- path: src/Internal/Admin/Settings/PaymentsProviders/PaymentGateway.php
-
-
message: '#^Call to function is_callable\(\) with array\{WC_Payment_Gateway, ''get_connection_url''\} will always evaluate to true\.$#'
identifier: function.alreadyNarrowedType
@@ -59301,66 +59175,6 @@ parameters:
count: 1
path: src/Internal/Admin/Settings/PaymentsProviders/PaymentGateway.php
- -
- message: '#^Call to an undefined static method Automattic\\WooCommerce\\Internal\\Logging\\SafeGlobalFunctionProxy\:\:wc_get_logger\(\)\.$#'
- identifier: staticMethod.notFound
- count: 1
- path: src/Internal/Admin/Settings/PaymentsProviders/Paymob.php
-
- -
- message: '#^Call to an undefined static method Automattic\\WooCommerce\\Internal\\Logging\\SafeGlobalFunctionProxy\:\:wc_get_logger\(\)\.$#'
- identifier: staticMethod.notFound
- count: 2
- path: src/Internal/Admin/Settings/PaymentsProviders/Payoneer.php
-
- -
- message: '#^Call to an undefined static method Automattic\\WooCommerce\\Internal\\Logging\\SafeGlobalFunctionProxy\:\:wc_get_logger\(\)\.$#'
- identifier: staticMethod.notFound
- count: 2
- path: src/Internal/Admin/Settings/PaymentsProviders/Paystack.php
-
- -
- message: '#^Call to an undefined static method Automattic\\WooCommerce\\Internal\\Logging\\SafeGlobalFunctionProxy\:\:wc_get_logger\(\)\.$#'
- identifier: staticMethod.notFound
- count: 2
- path: src/Internal/Admin/Settings/PaymentsProviders/Paytrail.php
-
- -
- message: '#^Call to an undefined static method Automattic\\WooCommerce\\Internal\\Logging\\SafeGlobalFunctionProxy\:\:wc_get_logger\(\)\.$#'
- identifier: staticMethod.notFound
- count: 2
- path: src/Internal/Admin/Settings/PaymentsProviders/Razorpay.php
-
- -
- message: '#^Call to an undefined static method Automattic\\WooCommerce\\Internal\\Logging\\SafeGlobalFunctionProxy\:\:wc_get_logger\(\)\.$#'
- identifier: staticMethod.notFound
- count: 3
- path: src/Internal/Admin/Settings/PaymentsProviders/Stripe.php
-
- -
- message: '#^Call to an undefined static method Automattic\\WooCommerce\\Internal\\Logging\\SafeGlobalFunctionProxy\:\:wc_get_logger\(\)\.$#'
- identifier: staticMethod.notFound
- count: 1
- path: src/Internal/Admin/Settings/PaymentsProviders/Tilopay.php
-
- -
- message: '#^Call to an undefined static method Automattic\\WooCommerce\\Internal\\Logging\\SafeGlobalFunctionProxy\:\:wc_get_logger\(\)\.$#'
- identifier: staticMethod.notFound
- count: 2
- path: src/Internal/Admin/Settings/PaymentsProviders/Visa.php
-
- -
- message: '#^Call to an undefined static method Automattic\\WooCommerce\\Internal\\Logging\\SafeGlobalFunctionProxy\:\:wc_get_logger\(\)\.$#'
- identifier: staticMethod.notFound
- count: 1
- path: src/Internal/Admin/Settings/PaymentsProviders/Vivacom.php
-
- -
- message: '#^Call to an undefined static method Automattic\\WooCommerce\\Internal\\Logging\\SafeGlobalFunctionProxy\:\:wc_get_logger\(\)\.$#'
- identifier: staticMethod.notFound
- count: 5
- path: src/Internal/Admin/Settings/PaymentsProviders/WooPayments.php
-
-
message: '#^Method Automattic\\WooCommerce\\Internal\\Admin\\Settings\\PaymentsProviders\\WooPayments\\WooPaymentsController\:\:register\(\) has no return type specified\.$#'
identifier: missingType.return
@@ -62628,12 +62442,6 @@ parameters:
count: 1
path: src/Internal/Logging/RemoteLogger.php
- -
- message: '#^Call to an undefined static method Automattic\\WooCommerce\\Internal\\Logging\\SafeGlobalFunctionProxy\:\:wc_get_logger\(\)\.$#'
- identifier: staticMethod.notFound
- count: 5
- path: src/Internal/Logging/RemoteLogger.php
-
-
message: '#^Call to an undefined static method Automattic\\WooCommerce\\Internal\\Logging\\SafeGlobalFunctionProxy\:\:wc_print_r\(\)\.$#'
identifier: staticMethod.notFound
diff --git a/plugins/woocommerce/src/Internal/Admin/Settings/PaymentsProviders.php b/plugins/woocommerce/src/Internal/Admin/Settings/PaymentsProviders.php
index 8c33f125d6e..b916110e3ea 100644
--- a/plugins/woocommerce/src/Internal/Admin/Settings/PaymentsProviders.php
+++ b/plugins/woocommerce/src/Internal/Admin/Settings/PaymentsProviders.php
@@ -11,6 +11,7 @@ use Automattic\WooCommerce\Internal\Admin\Settings\PaymentsProviders\AmazonPay;
use Automattic\WooCommerce\Internal\Admin\Settings\PaymentsProviders\Antom;
use Automattic\WooCommerce\Internal\Admin\Settings\PaymentsProviders\Eway;
use Automattic\WooCommerce\Internal\Admin\Settings\PaymentsProviders\GoCardless;
+use Automattic\WooCommerce\Internal\Admin\Settings\PaymentsProviders\Helcim;
use Automattic\WooCommerce\Internal\Admin\Settings\PaymentsProviders\HelioPay;
use Automattic\WooCommerce\Internal\Admin\Settings\PaymentsProviders\Klarna;
use Automattic\WooCommerce\Internal\Admin\Settings\PaymentsProviders\KlarnaCheckout;
@@ -132,6 +133,7 @@ class PaymentsProviders {
'airwallex_*' => Airwallex::class,
'vivawallet*' => Vivacom::class,
'tilopay' => Tilopay::class,
+ 'helcimjs' => Helcim::class,
'helio' => HelioPay::class,
'paytrail' => Paytrail::class,
'monei' => Monei::class,
@@ -172,6 +174,7 @@ class PaymentsProviders {
ExtensionSuggestions::AIRWALLEX => Airwallex::class,
ExtensionSuggestions::VIVA_WALLET => Vivacom::class,
ExtensionSuggestions::TILOPAY => Tilopay::class,
+ ExtensionSuggestions::HELCIM => Helcim::class,
ExtensionSuggestions::HELIOPAY => HelioPay::class,
ExtensionSuggestions::PAYTRAIL => Paytrail::class,
ExtensionSuggestions::MONEI => Monei::class,
diff --git a/plugins/woocommerce/src/Internal/Admin/Settings/PaymentsProviders/Helcim.php b/plugins/woocommerce/src/Internal/Admin/Settings/PaymentsProviders/Helcim.php
new file mode 100644
index 00000000000..eef792757f2
--- /dev/null
+++ b/plugins/woocommerce/src/Internal/Admin/Settings/PaymentsProviders/Helcim.php
@@ -0,0 +1,120 @@
+<?php
+declare( strict_types=1 );
+
+namespace Automattic\WooCommerce\Internal\Admin\Settings\PaymentsProviders;
+
+use Automattic\WooCommerce\Internal\Logging\SafeGlobalFunctionProxy;
+use Throwable;
+use WC_Payment_Gateway;
+
+defined( 'ABSPATH' ) || exit;
+
+/**
+ * Helcim payment gateway provider class.
+ *
+ * This class handles all the custom logic for the Helcim payment gateway provider.
+ *
+ * @internal
+ */
+class Helcim extends PaymentGateway {
+
+ /**
+ * Check if the payment gateway has a payments processor account connected.
+ *
+ * @param WC_Payment_Gateway $payment_gateway The payment gateway object.
+ *
+ * @return bool True if the payment gateway account is connected, false otherwise.
+ * If the payment gateway does not provide the information, it will return true.
+ */
+ public function is_account_connected( WC_Payment_Gateway $payment_gateway ): bool {
+ try {
+ $sandbox_mode = $this->is_helcim_in_sandbox_mode( $payment_gateway );
+ // Let null results bubble up to the parent class.
+ if ( null !== $sandbox_mode ) {
+ $token_key = $sandbox_mode ? 'sandbox_api_token' : 'live_api_token';
+
+ return '' !== trim( (string) $payment_gateway->get_option( $token_key, '' ) );
+ }
+ } catch ( Throwable $e ) {
+ // Do nothing but log so we can investigate.
+ SafeGlobalFunctionProxy::wc_get_logger()->debug(
+ 'Failed to determine if gateway has an account connected: ' . $e->getMessage(),
+ array(
+ 'gateway' => $payment_gateway->id,
+ 'source' => 'settings-payments',
+ 'exception' => $e,
+ )
+ );
+ }
+
+ return parent::is_account_connected( $payment_gateway );
+ }
+
+ /**
+ * Determine if the payment gateway is in test mode.
+ *
+ * @param WC_Payment_Gateway $payment_gateway The payment gateway object.
+ *
+ * @return bool True if the payment gateway is in test mode, false otherwise.
+ */
+ public function is_in_test_mode( WC_Payment_Gateway $payment_gateway ): bool {
+ return $this->is_helcim_in_sandbox_mode( $payment_gateway ) ?? parent::is_in_test_mode( $payment_gateway );
+ }
+
+ /**
+ * Try to determine if the payment gateway is in test mode onboarding (aka sandbox).
+ *
+ * This is a best-effort attempt, as there is no standard way to determine this.
+ * Trust the true value, but don't consider a false value as definitive.
+ *
+ * @param WC_Payment_Gateway $payment_gateway The payment gateway object.
+ *
+ * @return bool True if the payment gateway is in test mode onboarding, false otherwise.
+ */
+ public function is_in_test_mode_onboarding( WC_Payment_Gateway $payment_gateway ): bool {
+ return $this->is_helcim_in_sandbox_mode( $payment_gateway ) ?? parent::is_in_test_mode_onboarding( $payment_gateway );
+ }
+
+ /**
+ * Check if the Helcim payment gateway is in sandbox mode.
+ *
+ * @param WC_Payment_Gateway $payment_gateway The payment gateway object.
+ *
+ * @return ?bool True if the payment gateway is in sandbox mode, false otherwise.
+ * Null if the environment could not be determined.
+ */
+ private function is_helcim_in_sandbox_mode( WC_Payment_Gateway $payment_gateway ): ?bool {
+ try {
+ // Helcim stores the active environment ('sandbox'|'live') as a gateway option,
+ // read through the gateway's own get_option() so we follow whatever option key
+ // and precedence the extension uses internally.
+ $environment = $payment_gateway->get_option( 'environment' );
+ if ( ! is_string( $environment ) ) {
+ return null;
+ }
+
+ $environment = strtolower( trim( $environment ) );
+ if ( 'sandbox' === $environment ) {
+ return true;
+ }
+ if ( 'live' === $environment ) {
+ return false;
+ }
+
+ return null;
+ } catch ( Throwable $e ) {
+ // Do nothing but log so we can investigate.
+ SafeGlobalFunctionProxy::wc_get_logger()->debug(
+ 'Failed to determine if gateway is in sandbox mode: ' . $e->getMessage(),
+ array(
+ 'gateway' => $payment_gateway->id,
+ 'source' => 'settings-payments',
+ 'exception' => $e,
+ )
+ );
+ }
+
+ // Let the caller know that we couldn't determine the environment.
+ return null;
+ }
+}
diff --git a/plugins/woocommerce/src/Internal/Admin/Suggestions/PaymentsExtensionSuggestions.php b/plugins/woocommerce/src/Internal/Admin/Suggestions/PaymentsExtensionSuggestions.php
index 9d7c9abaf14..584f2255178 100644
--- a/plugins/woocommerce/src/Internal/Admin/Suggestions/PaymentsExtensionSuggestions.php
+++ b/plugins/woocommerce/src/Internal/Admin/Suggestions/PaymentsExtensionSuggestions.php
@@ -45,6 +45,7 @@ class PaymentsExtensionSuggestions {
const CLEARPAY = 'clearpay';
const KLARNA = 'klarna';
const KLARNA_CHECKOUT = 'klarna_checkout';
+ const HELCIM = 'helcim';
const HELIOPAY = 'heliopay';
const MONEI = 'monei';
const COINBASE = 'coinbase';
@@ -177,6 +178,7 @@ class PaymentsExtensionSuggestions {
),
),
),
+ self::HELCIM,
self::PAYPAL_WALLET,
self::AFFIRM,
self::AFTERPAY,
@@ -213,6 +215,7 @@ class PaymentsExtensionSuggestions {
self::SQUARE, // Use the default details.
self::VISA,
self::AIRWALLEX,
+ self::HELCIM,
self::PAYPAL_WALLET,
self::AMAZON_PAY,
self::AFFIRM,
@@ -4132,6 +4135,38 @@ class PaymentsExtensionSuggestions {
),
),
),
+ self::HELCIM => array(
+ '_type' => self::TYPE_PSP,
+ 'title' => esc_html__( 'Helcim', 'woocommerce' ),
+ 'description' => esc_html__( 'Accept credit cards and Google Pay directly on your store with zero monthly fees. Save up to 25% using Helcim’s transparent interchange-plus pricing.', 'woocommerce' ),
+ 'icon' => plugins_url( 'assets/images/onboarding/icons/helcim.svg', WC_PLUGIN_FILE ),
+ 'plugin' => array(
+ '_type' => self::PLUGIN_TYPE_WPORG,
+ 'slug' => 'helcim-commerce-for-woocommerce',
+ ),
+ 'links' => array(
+ array(
+ '_type' => PaymentsProviders::LINK_TYPE_PRICING,
+ 'url' => 'https://www.helcim.com/pricing/',
+ ),
+ array(
+ '_type' => PaymentsProviders::LINK_TYPE_ABOUT,
+ 'url' => 'https://woocommerce.com/products/helcim-commerce-for-woocommerce/',
+ ),
+ array(
+ '_type' => PaymentsProviders::LINK_TYPE_TERMS,
+ 'url' => 'https://legal.helcim.com/terms-of-service/',
+ ),
+ array(
+ '_type' => PaymentsProviders::LINK_TYPE_DOCS,
+ 'url' => 'https://woocommerce.com/document/helcim-commerce-for-woocommerce/',
+ ),
+ array(
+ '_type' => PaymentsProviders::LINK_TYPE_SUPPORT,
+ 'url' => 'https://woocommerce.com/my-account/contact-support/?select=helcim-commerce-for-woocommerce',
+ ),
+ ),
+ ),
self::HELIOPAY => array(
'_type' => self::TYPE_CRYPTO,
'title' => esc_html__( 'Helio Pay', 'woocommerce' ),
diff --git a/plugins/woocommerce/src/Internal/Logging/SafeGlobalFunctionProxy.php b/plugins/woocommerce/src/Internal/Logging/SafeGlobalFunctionProxy.php
index 061eef6a0dd..fc1c864e418 100644
--- a/plugins/woocommerce/src/Internal/Logging/SafeGlobalFunctionProxy.php
+++ b/plugins/woocommerce/src/Internal/Logging/SafeGlobalFunctionProxy.php
@@ -11,6 +11,8 @@ namespace Automattic\WooCommerce\Internal\Logging;
*
* @since 9.4.0
* @package Automattic\WooCommerce\Internal\Logging
+ *
+ * @method static \WC_Logger_Interface wc_get_logger()
*/
class SafeGlobalFunctionProxy {
diff --git a/plugins/woocommerce/tests/php/src/Internal/Admin/Settings/PaymentsProviders/HelcimTest.php b/plugins/woocommerce/tests/php/src/Internal/Admin/Settings/PaymentsProviders/HelcimTest.php
new file mode 100644
index 00000000000..0993f4e3936
--- /dev/null
+++ b/plugins/woocommerce/tests/php/src/Internal/Admin/Settings/PaymentsProviders/HelcimTest.php
@@ -0,0 +1,208 @@
+<?php
+declare( strict_types=1 );
+
+namespace Automattic\WooCommerce\Tests\Internal\Admin\Settings\PaymentsProviders;
+
+use Automattic\WooCommerce\Internal\Admin\Settings\PaymentsProviders\Helcim;
+use Automattic\WooCommerce\Proxies\LegacyProxy;
+use Automattic\WooCommerce\Testing\Tools\DependencyManagement\MockableLegacyProxy;
+use Automattic\WooCommerce\Testing\Tools\TestingContainer;
+use Automattic\WooCommerce\Tests\Internal\Admin\Settings\Mocks\FakePaymentGateway;
+use PHPUnit\Framework\MockObject\MockObject;
+use WC_Unit_Test_Case;
+
+/**
+ * Helcim payment gateway provider service test.
+ *
+ * @class Helcim
+ */
+class HelcimTest extends WC_Unit_Test_Case {
+
+ /**
+ * @var MockableLegacyProxy|MockObject
+ */
+ protected $mockable_proxy;
+
+ /**
+ * The System Under Test.
+ *
+ * @var Helcim
+ */
+ protected $sut;
+
+ /**
+ * Set up test.
+ */
+ public function setUp(): void {
+ parent::setUp();
+
+ /**
+ * TestingContainer instance.
+ *
+ * @var TestingContainer $container
+ */
+ $container = wc_get_container();
+
+ $this->mockable_proxy = $container->get( LegacyProxy::class );
+
+ $this->sut = new Helcim( $this->mockable_proxy );
+ }
+
+ /**
+ * @testdox Should report account connected when a sandbox API token is set in sandbox mode.
+ */
+ public function test_is_account_connected_true_when_sandbox_token_set(): void {
+ $fake_gateway = new FakePaymentGateway(
+ 'helcimjs',
+ array(
+ 'settings' => array(
+ 'environment' => 'sandbox',
+ 'sandbox_api_token' => 'bogus_sandbox_token',
+ ),
+ ),
+ );
+
+ $this->assertTrue( $this->sut->is_account_connected( $fake_gateway ) );
+ }
+
+ /**
+ * @testdox Should report account not connected when no sandbox API token is set in sandbox mode.
+ */
+ public function test_is_account_connected_false_when_sandbox_token_missing(): void {
+ $fake_gateway = new FakePaymentGateway(
+ 'helcimjs',
+ array(
+ 'settings' => array(
+ 'environment' => 'sandbox',
+ ),
+ ),
+ );
+
+ $this->assertFalse( $this->sut->is_account_connected( $fake_gateway ) );
+ }
+
+ /**
+ * @testdox Should report account connected when a live API token is set in live mode.
+ */
+ public function test_is_account_connected_true_when_live_token_set(): void {
+ $fake_gateway = new FakePaymentGateway(
+ 'helcimjs',
+ array(
+ 'settings' => array(
+ 'environment' => 'live',
+ 'live_api_token' => 'bogus_live_token',
+ ),
+ ),
+ );
+
+ $this->assertTrue( $this->sut->is_account_connected( $fake_gateway ) );
+ }
+
+ /**
+ * @testdox Should be in test mode when the environment is sandbox.
+ */
+ public function test_is_in_test_mode_true_in_sandbox(): void {
+ $fake_gateway = new FakePaymentGateway(
+ 'helcimjs',
+ array(
+ 'settings' => array(
+ 'environment' => 'sandbox',
+ ),
+ ),
+ );
+
+ $this->assertTrue( $this->sut->is_in_test_mode( $fake_gateway ) );
+ }
+
+ /**
+ * @testdox Should not be in test mode when the environment is live.
+ */
+ public function test_is_in_test_mode_false_in_live(): void {
+ $fake_gateway = new FakePaymentGateway(
+ 'helcimjs',
+ array(
+ 'settings' => array(
+ 'environment' => 'live',
+ ),
+ ),
+ );
+
+ $this->assertFalse( $this->sut->is_in_test_mode( $fake_gateway ) );
+ }
+
+ /**
+ * @testdox Should fall back to the generic account-connected heuristic when the environment option is entirely absent.
+ */
+ public function test_is_account_connected_falls_back_when_environment_option_missing(): void {
+ // Simulates a pre-v5 Helcim install: same gateway id, but no 'environment'
+ // option was ever registered, so it's absent from both settings and form_fields.
+ $fake_gateway = new FakePaymentGateway(
+ 'helcimjs',
+ array(
+ 'settings' => array(
+ 'legacy_api_token' => 'real_legacy_live_token',
+ ),
+ 'account_connected' => true,
+ ),
+ );
+
+ $this->assertTrue( $this->sut->is_account_connected( $fake_gateway ) );
+ }
+
+ /**
+ * @testdox Should fall back to the generic account-connected heuristic for an unrecognized environment value.
+ */
+ public function test_is_account_connected_falls_back_for_unrecognized_environment(): void {
+ // account_connected is true here (not false) so the assertion can't pass
+ // by coincidence: an incorrectly-sandbox or incorrectly-live read would
+ // look up an absent token option and return false either way, while only
+ // a correct null (unrecognized -> defer to parent) read reaches this true.
+ $fake_gateway = new FakePaymentGateway(
+ 'helcimjs',
+ array(
+ 'settings' => array(
+ 'environment' => 'staging',
+ ),
+ 'account_connected' => true,
+ ),
+ );
+
+ $this->assertTrue( $this->sut->is_account_connected( $fake_gateway ) );
+ }
+
+ /**
+ * @testdox Should fall back to the generic test-mode heuristic for an unrecognized environment value.
+ */
+ public function test_is_in_test_mode_falls_back_for_unrecognized_environment(): void {
+ $fake_gateway = new FakePaymentGateway(
+ 'helcimjs',
+ array(
+ 'settings' => array(
+ 'environment' => 'staging',
+ ),
+ 'test_mode' => true,
+ ),
+ );
+
+ $this->assertTrue( $this->sut->is_in_test_mode( $fake_gateway ) );
+ }
+
+ /**
+ * @testdox Should report the same test-mode-onboarding value as test mode.
+ */
+ public function test_is_in_test_mode_onboarding_matches_is_in_test_mode(): void {
+ $fake_gateway = new FakePaymentGateway(
+ 'helcimjs',
+ array(
+ 'settings' => array(
+ 'environment' => 'sandbox',
+ ),
+ ),
+ );
+
+ $this->assertSame(
+ $this->sut->is_in_test_mode( $fake_gateway ),
+ $this->sut->is_in_test_mode_onboarding( $fake_gateway )
+ );
+ }
+}
diff --git a/plugins/woocommerce/tests/php/src/Internal/Admin/Settings/PaymentsRestControllerIntegrationTest.php b/plugins/woocommerce/tests/php/src/Internal/Admin/Settings/PaymentsRestControllerIntegrationTest.php
index e239c2aa75b..03003feb1b2 100644
--- a/plugins/woocommerce/tests/php/src/Internal/Admin/Settings/PaymentsRestControllerIntegrationTest.php
+++ b/plugins/woocommerce/tests/php/src/Internal/Admin/Settings/PaymentsRestControllerIntegrationTest.php
@@ -464,7 +464,7 @@ class PaymentsRestControllerIntegrationTest extends WC_Unit_Test_Case {
// We also have the 3 offline payment methods.
$this->assertCount( 3, $data['offline_payment_methods'] );
// We only have PSPs because there is no payment gateway enabled.
- $this->assertCount( 4, $data['suggestions'] );
+ $this->assertCount( 5, $data['suggestions'] );
// Assert we get the suggestion categories.
$this->assertCount( 4, $data['suggestion_categories'] );
@@ -510,7 +510,7 @@ class PaymentsRestControllerIntegrationTest extends WC_Unit_Test_Case {
// We also have the 3 offline payment methods.
$this->assertCount( 3, $data['offline_payment_methods'] );
// We get all the suggestions.
- $this->assertCount( 8, $data['suggestions'] );
+ $this->assertCount( 9, $data['suggestions'] );
// Assert we get the suggestion categories.
$this->assertCount( 4, $data['suggestion_categories'] );
@@ -680,7 +680,7 @@ class PaymentsRestControllerIntegrationTest extends WC_Unit_Test_Case {
// We also have the 3 offline payment methods.
$this->assertCount( 3, $data['offline_payment_methods'] );
// We get all the suggestions.
- $this->assertCount( 8, $data['suggestions'] );
+ $this->assertCount( 9, $data['suggestions'] );
// Assert we get the suggestion categories.
$this->assertCount( 4, $data['suggestion_categories'] );
@@ -878,8 +878,8 @@ class PaymentsRestControllerIntegrationTest extends WC_Unit_Test_Case {
// We also have the 3 offline payment methods.
$this->assertCount( 3, $data['offline_payment_methods'] );
- // We don't get BNPL suggestions since WooPayments is active (even if not enabled), so only 5 suggestions are returned.
- $this->assertCount( 5, $data['suggestions'] );
+ // We don't get BNPL suggestions since WooPayments is active (even if not enabled), so only 6 suggestions are returned.
+ $this->assertCount( 6, $data['suggestions'] );
// Assert we get the suggestion categories.
$this->assertCount( 4, $data['suggestion_categories'] );
@@ -1092,8 +1092,8 @@ class PaymentsRestControllerIntegrationTest extends WC_Unit_Test_Case {
// We also have the 3 offline payment methods.
$this->assertCount( 3, $data['offline_payment_methods'] );
- // We don't get BNPL suggestions since WooPayments is active, so only 5 suggestions are returned.
- $this->assertCount( 5, $data['suggestions'] );
+ // We don't get BNPL suggestions since WooPayments is active, so only 6 suggestions are returned.
+ $this->assertCount( 6, $data['suggestions'] );
// Assert we get the suggestion categories.
$this->assertCount( 4, $data['suggestion_categories'] );
@@ -1261,8 +1261,8 @@ class PaymentsRestControllerIntegrationTest extends WC_Unit_Test_Case {
// We also have the 3 offline payment methods.
$this->assertCount( 3, $data['offline_payment_methods'] );
- // We get BNPL suggestions since WooPayments or Stripe are not active, so 7 suggestions are returned.
- $this->assertCount( 7, $data['suggestions'] );
+ // We get BNPL suggestions since WooPayments or Stripe are not active, so 8 suggestions are returned.
+ $this->assertCount( 8, $data['suggestions'] );
// Assert we get the suggestion categories.
$this->assertCount( 4, $data['suggestion_categories'] );
diff --git a/plugins/woocommerce/tests/php/src/Internal/Admin/Suggestions/PaymentsExtensionSuggestionsTest.php b/plugins/woocommerce/tests/php/src/Internal/Admin/Suggestions/PaymentsExtensionSuggestionsTest.php
index ba4447122db..48b1bfcff4f 100644
--- a/plugins/woocommerce/tests/php/src/Internal/Admin/Suggestions/PaymentsExtensionSuggestionsTest.php
+++ b/plugins/woocommerce/tests/php/src/Internal/Admin/Suggestions/PaymentsExtensionSuggestionsTest.php
@@ -128,8 +128,8 @@ class PaymentsExtensionSuggestionsTest extends WC_Unit_Test_Case {
public function data_provider_get_country_extensions_count_with_merchant_selling_online(): array {
// The counts are based on the data in PaymentExtensionSuggestions::$country_extensions.
$country_suggestions_count = array(
- 'CA' => 10,
- 'US' => 11,
+ 'CA' => 11,
+ 'US' => 12,
'GB' => 15,
'AT' => 13,
'BE' => 11,
@@ -521,8 +521,8 @@ class PaymentsExtensionSuggestionsTest extends WC_Unit_Test_Case {
public function data_provider_get_country_extensions_count_with_merchant_selling_offline(): array {
// The counts are based on the data in PaymentExtensionSuggestions::$country_extensions.
$country_suggestions_count = array(
- 'CA' => 10,
- 'US' => 11,
+ 'CA' => 11,
+ 'US' => 12,
'GB' => 15,
'AT' => 13,
'BE' => 11,
@@ -1027,6 +1027,103 @@ class PaymentsExtensionSuggestionsTest extends WC_Unit_Test_Case {
);
}
+ /**
+ * @testdox Helcim is the last PSP suggestion in $country_code.
+ *
+ * @dataProvider data_provider_helcim_supported_countries
+ *
+ * @param string $country_code ISO 3166-1 alpha-2 country code.
+ */
+ public function test_helcim_is_last_psp_suggestion_in_supported_countries( string $country_code ): void {
+ $extensions = $this->sut->get_country_extensions( $country_code );
+ $extensions_by_id = array_column( $extensions, null, 'id' );
+ $psp_ids = array_column(
+ array_filter(
+ $extensions,
+ static fn( array $extension ): bool => PaymentsExtensionSuggestions::TYPE_PSP === $extension['_type']
+ ),
+ 'id'
+ );
+ $helcim = $extensions_by_id[ PaymentsExtensionSuggestions::HELCIM ] ?? null;
+
+ $this->assertSame(
+ PaymentsExtensionSuggestions::HELCIM,
+ end( $psp_ids ),
+ "Helcim should be the final PSP suggestion in {$country_code}."
+ );
+ $this->assertIsArray( $helcim, "Helcim should be suggested in {$country_code}." );
+ if ( ! is_array( $helcim ) ) {
+ return;
+ }
+
+ $this->assertNotContains(
+ PaymentsExtensionSuggestions::TAG_PREFERRED,
+ $helcim['tags'],
+ "Helcim should remain in other payment options for {$country_code}."
+ );
+ }
+
+ /**
+ * Data provider for Helcim's supported countries.
+ *
+ * @return array<string, array{string}>
+ */
+ public function data_provider_helcim_supported_countries(): array {
+ return array(
+ 'Canada' => array( 'CA' ),
+ 'United States' => array( 'US' ),
+ );
+ }
+
+ /**
+ * @testdox Helcim has complete base suggestion details.
+ */
+ public function test_helcim_has_complete_base_details(): void {
+ $extension = $this->sut->get_by_id( 'helcim' );
+
+ $this->assertIsArray( $extension );
+ if ( ! is_array( $extension ) ) {
+ return;
+ }
+
+ $this->assertSame( PaymentsExtensionSuggestions::TYPE_PSP, $extension['_type'] );
+ $this->assertSame(
+ array(
+ '_type' => PaymentsExtensionSuggestions::PLUGIN_TYPE_WPORG,
+ 'slug' => 'helcim-commerce-for-woocommerce',
+ ),
+ $extension['plugin']
+ );
+ $this->assertEqualsCanonicalizing(
+ array(
+ array(
+ '_type' => PaymentsProviders::LINK_TYPE_PRICING,
+ 'url' => 'https://www.helcim.com/pricing/',
+ ),
+ array(
+ '_type' => PaymentsProviders::LINK_TYPE_ABOUT,
+ 'url' => 'https://woocommerce.com/products/helcim-commerce-for-woocommerce/',
+ ),
+ array(
+ '_type' => PaymentsProviders::LINK_TYPE_TERMS,
+ 'url' => 'https://legal.helcim.com/terms-of-service/',
+ ),
+ array(
+ '_type' => PaymentsProviders::LINK_TYPE_DOCS,
+ 'url' => 'https://woocommerce.com/document/helcim-commerce-for-woocommerce/',
+ ),
+ array(
+ '_type' => PaymentsProviders::LINK_TYPE_SUPPORT,
+ 'url' => 'https://woocommerce.com/my-account/contact-support/?select=helcim-commerce-for-woocommerce',
+ ),
+ ),
+ $extension['links']
+ );
+ $this->assertNotEmpty( $extension['icon'] );
+ $this->assertNotEmpty( $extension['title'] );
+ $this->assertNotEmpty( $extension['description'] );
+ }
+
/**
* Test getting payment extension suggestions by ID.
*/