{"id":337135,"date":"2026-07-15T06:13:17","date_gmt":"2026-07-15T06:13:17","guid":{"rendered":"https:\/\/wordpress.org\/plugins\/pay4all-twint\/"},"modified":"2026-07-16T15:24:26","modified_gmt":"2026-07-16T15:24:26","slug":"pay4all-for-twint","status":"publish","type":"plugin","link":"https:\/\/scn.wordpress.org\/plugins\/pay4all-for-twint\/","author":23516902,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"1.3.0","stable_tag":"1.3.0","tested":"7.0.1","requires":"5.9","requires_php":"7.4","requires_plugins":null,"header_name":"Pay4All for TWINT","header_author":"Pay4All","header_description":"Adds the TWINT payment gateway to Pay4All Shop (order-form-based commerce). Free, no license, no domain activation. TWINT is a trademark of TWINT AG; this plugin is not affiliated with TWINT AG.","assets_banners_color":"9185c0","last_updated":"2026-07-16 15:24:26","external_support_url":"","external_repository_url":"","donate_link":"","header_plugin_uri":"","header_author_uri":"https:\/\/pay4all.ch\/","rating":0,"author_block_rating":0,"active_installs":0,"downloads":85,"num_ratings":0,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","installation","faq","changelog"],"tags":{"1.0.0":{"tag":"1.0.0","author":"pay4all","date":"2026-07-15 06:13:07"},"1.1.0":{"tag":"1.1.0","author":"pay4all","date":"2026-07-15 10:08:34"},"1.2.0":{"tag":"1.2.0","author":"pay4all","date":"2026-07-16 07:05:13"},"1.3.0":{"tag":"1.3.0","author":"pay4all","date":"2026-07-16 15:24:26"}},"upgrade_notice":{"1.3.0":"<p>Security release. Merchant PEM moved to a random filename with chmod 0600 and dual .htaccess+index.php protection ; certificate password now encrypted at rest with authenticated encryption ; TWINT return signature expires after 30 min. Merchants should re-open the wizard and re-save their password (and optionally re-upload the p12) to benefit from the new cert filename immediately \u2014 the plugin also keeps working seamlessly with legacy stored values until they do.<\/p>","1.2.0":"<p>Adds the \u00ab Commande \u00bb wizard tab (unified Pay4All Shop + WooCommerce post-payment status configuration), lets the plugin boot standalone without Pay4All Shop, and wires a Pay4All Pro shortcut. No config change, no manual action.<\/p>","1.1.0":"<p>Rewrites the TWINT SOAP client in non-WSDL mode (no <code>.xml<\/code> schema files shipped) and stops logging SOAP request\/response payloads. No config change, no manual action.<\/p>","1.0.0":"<p>Initial release on WordPress.org. Free, no license, no domain activation.<\/p>"},"ratings":[],"assets_icons":{"icon-128x128.png":{"filename":"icon-128x128.png","revision":3608678,"resolution":"128x128","location":"assets","locale":"","width":128,"height":128},"icon-256x256.png":{"filename":"icon-256x256.png","revision":3608678,"resolution":"256x256","location":"assets","locale":"","width":256,"height":256}},"assets_banners":{"banner-1544x500.png":{"filename":"banner-1544x500.png","revision":3608289,"resolution":"1544x500","location":"assets","locale":"","width":1544,"height":500},"banner-772x250.png":{"filename":"banner-772x250.png","revision":3608289,"resolution":"772x250","location":"assets","locale":"","width":772,"height":250}},"assets_blueprints":{},"all_blocks":[],"tagged_versions":["1.0.0","1.1.0","1.2.0","1.3.0"],"block_files":[],"assets_screenshots":{"screenshot-1.png":{"filename":"screenshot-1.png","revision":3608289,"resolution":"1","location":"assets","locale":"","width":1152,"height":1723},"screenshot-2.png":{"filename":"screenshot-2.png","revision":3608289,"resolution":"2","location":"assets","locale":"","width":1167,"height":1574},"screenshot-3.png":{"filename":"screenshot-3.png","revision":3608289,"resolution":"3","location":"assets","locale":"","width":1168,"height":1139},"screenshot-4.png":{"filename":"screenshot-4.png","revision":3608289,"resolution":"4","location":"assets","locale":"","width":1123,"height":1847},"screenshot-5.png":{"filename":"screenshot-5.png","revision":3608289,"resolution":"5","location":"assets","locale":"","width":1138,"height":1504}},"screenshots":{"1":"Setup wizard, tab \u00ab Pr\u00e9paration \u00bb : the merchant is walked through the TWINT portal signup, store creation and certificate generation before any credential is asked for.","2":"Setup wizard, tab \u00ab Identification \u00bb : the merchant pastes the store UID \/ cash-register ID and uploads the .p12 certificate + password ; the plugin auto-converts to PEM and runs the connection test.","3":"Setup wizard, tab \u00ab \u00c9tat \u00bb : the \u00ab Connect\u00e9 \u00e0 TWINT \u00bb card confirms the last test result and lets the merchant re-run the connection test at any time.","4":"WooCommerce sub-menu \u00ab Configurer TWINT pour WooCommerce \u00bb : shortcut to the WooCommerce gateway settings when Pay4All Pro is installed alongside this plugin.","5":"Pay4All Shop payment methods : TWINT appears as an activable payment method on the free Pay4All Shop plugin's checkout method picker."}},"plugin_section":[],"plugin_tags":[1890,271599,507,9090,193458],"plugin_category":[45],"plugin_contributors":[268195],"plugin_business_model":[],"class_list":["post-337135","plugin","type-plugin","status-publish","hentry","plugin_tags-gateway","plugin_tags-pay4all","plugin_tags-payment","plugin_tags-switzerland","plugin_tags-twint","plugin_category-ecommerce","plugin_contributors-pay4all","plugin_committers-pay4all"],"banners":{"banner":"https:\/\/ps.w.org\/pay4all-for-twint\/assets\/banner-772x250.png?rev=3608289","banner_2x":"https:\/\/ps.w.org\/pay4all-for-twint\/assets\/banner-1544x500.png?rev=3608289","banner_rtl":false,"banner_2x_rtl":false},"icons":{"svg":false,"icon":"https:\/\/ps.w.org\/pay4all-for-twint\/assets\/icon-128x128.png?rev=3608678","icon_2x":"https:\/\/ps.w.org\/pay4all-for-twint\/assets\/icon-256x256.png?rev=3608678","generated":false},"screenshots":[{"src":"https:\/\/ps.w.org\/pay4all-for-twint\/assets\/screenshot-1.png?rev=3608289","caption":"Setup wizard, tab \u00ab Pr\u00e9paration \u00bb : the merchant is walked through the TWINT portal signup, store creation and certificate generation before any credential is asked for."},{"src":"https:\/\/ps.w.org\/pay4all-for-twint\/assets\/screenshot-2.png?rev=3608289","caption":"Setup wizard, tab \u00ab Identification \u00bb : the merchant pastes the store UID \/ cash-register ID and uploads the .p12 certificate + password ; the plugin auto-converts to PEM and runs the connection test."},{"src":"https:\/\/ps.w.org\/pay4all-for-twint\/assets\/screenshot-3.png?rev=3608289","caption":"Setup wizard, tab \u00ab \u00c9tat \u00bb : the \u00ab Connect\u00e9 \u00e0 TWINT \u00bb card confirms the last test result and lets the merchant re-run the connection test at any time."},{"src":"https:\/\/ps.w.org\/pay4all-for-twint\/assets\/screenshot-4.png?rev=3608289","caption":"WooCommerce sub-menu \u00ab Configurer TWINT pour WooCommerce \u00bb : shortcut to the WooCommerce gateway settings when Pay4All Pro is installed alongside this plugin."},{"src":"https:\/\/ps.w.org\/pay4all-for-twint\/assets\/screenshot-5.png?rev=3608289","caption":"Pay4All Shop payment methods : TWINT appears as an activable payment method on the free Pay4All Shop plugin's checkout method picker."}],"raw_content":"<!--section=description-->\n<p>Pay4All for TWINT adds TWINT as a payment method to the free Pay4All Shop plugin (order-form-based checkouts).<\/p>\n\n<p>TWINT is a trademark of TWINT AG (Switzerland). This plugin is an independent integration and is not affiliated with, endorsed by, or sponsored by TWINT AG.<\/p>\n\n<p>Main features:<\/p>\n\n<ul>\n<li>TWINT integration for the free Pay4All Shop plugin (order forms with TWINT payment).<\/li>\n<li>Step-by-step setup wizard: TWINT credentials, certificate upload, connection test, status page.<\/li>\n<li>Automatic conversion of the .p12 certificate delivered by TWINT to PEM format.<\/li>\n<li>Cash register enrollment and connection test built into the wizard.<\/li>\n<li>Localized payment instructions in French, German, Italian and English.<\/li>\n<\/ul>\n\n<p>To use this plugin you need:<\/p>\n\n<ul>\n<li>the free Pay4All Shop plugin installed and active.<\/li>\n<li>a TWINT merchant account with a configured store and an active certificate (see https:\/\/portal.twint.ch).<\/li>\n<\/ul>\n\n<p>There is no license fee, no subscription, no domain activation. Install, activate, run the wizard, done.<\/p>\n\n<h4>WooCommerce support<\/h4>\n\n<p>The WooCommerce payment-gateway integration (classic checkout, Checkout Blocks, HPOS, refunds, background monitoring) is not shipped inside this free plugin. It lives inside the paid companion \u00ab Pay4All Pro \u00bb, distributed outside WordPress.org from https:\/\/pay4all.ch\/. Install Pay4All Pro next to this plugin to enable the WooCommerce TWINT gateway.<\/p>\n\n<h3>External services<\/h3>\n\n<p>This plugin connects to the TWINT merchant SOAP API (operated by TWINT AG) to authorize, capture, refund and reconcile payments. Without a valid TWINT merchant account and certificate, the gateway cannot process payments.<\/p>\n\n<p><strong>What it is used for<\/strong><\/p>\n\n<ul>\n<li>Every TWINT payment initiated from a Pay4All Shop form is forwarded to the TWINT SOAP API for authorization and status polling (payment start, background status polling, refund, cancellation).<\/li>\n<li>Nothing else is sent to any external service.<\/li>\n<\/ul>\n\n<p><strong>What data is sent, and when<\/strong><\/p>\n\n<ul>\n<li>On payment start: the order number, the amount, the currency, the store UID (from your TWINT merchant account), the cash-register ID and \u2014 depending on your TWINT setup \u2014 the customer's e-mail or phone number.<\/li>\n<li>On status polling and reconciliation: only the TWINT transaction identifier returned by the previous call.<\/li>\n<li>On refund: the TWINT transaction identifier plus the amount to refund.<\/li>\n<li>No login credentials, no plugin telemetry, no site metadata are sent.<\/li>\n<\/ul>\n\n<p><strong>Where the data is sent<\/strong><\/p>\n\n<p>The plugin talks to one of three TWINT-owned endpoints depending on your <code>T4A_ENVIRONMENT<\/code> setting:<\/p>\n\n<ul>\n<li>Production: <code>https:\/\/service.twint.ch\/merchant\/service\/TWINTMerchantServiceV8_4<\/code><\/li>\n<li>Integration \/ staging: <code>https:\/\/service-int.twint.ch\/merchant\/service\/TWINTMerchantServiceV8_4<\/code><\/li>\n<li>Pre-authorization test: <code>https:\/\/service-pat.twint.ch\/merchant\/service\/TWINTMerchantServiceV8_4<\/code><\/li>\n<\/ul>\n\n<p><strong>Terms of use and privacy policy for TWINT<\/strong><\/p>\n\n<ul>\n<li>TWINT terms of use: https:\/\/www.twint.ch\/en\/legal\/<\/li>\n<li>TWINT data protection: https:\/\/www.twint.ch\/en\/data-protection\/<\/li>\n<\/ul>\n\n<p>No other external service is contacted by this plugin.<\/p>\n\n<h3>Source code<\/h3>\n\n<p>The complete, non-minified source of this plugin is bundled inside the ZIP itself. Every PHP file is human-readable and directly editable. The JavaScript bundles under <code>assets\/js\/<\/code> are shipped in a pretty-printed, non-minified form so you can read and modify them without a build step.<\/p>\n\n<p>No compiler, transpiler or bundler is required to work on this plugin.<\/p>\n\n<!--section=installation-->\n<ol>\n<li>Upload the <code>pay4all-for-twint<\/code> folder to <code>\/wp-content\/plugins\/<\/code> (or install the ZIP from Plugins &gt; Add New).<\/li>\n<li>Activate \"Pay4All for TWINT\" from the WordPress Plugins screen.<\/li>\n<li>Install and activate the free \"Pay4All Shop\" plugin (required companion).<\/li>\n<li>Open the \"Pay4All TWINT\" menu in the admin and follow the wizard: TWINT credentials, certificate upload, status.<\/li>\n<\/ol>\n\n<p>Server requirements:<\/p>\n\n<ul>\n<li>PHP 7.4 or higher (8.x recommended).<\/li>\n<li>PHP extensions: <code>openssl<\/code>, <code>soap<\/code>, <code>curl<\/code>.<\/li>\n<li>WordPress 5.9 or higher.<\/li>\n<li>Pay4All Shop plugin (required).<\/li>\n<\/ul>\n\n<!--section=faq-->\n<dl>\n<dt id=\"does%20the%20plugin%20work%20without%20pay4all%20shop%3F\"><h3>Does the plugin work without Pay4All Shop?<\/h3><\/dt>\n<dd><p>No. Pay4All Shop is a required companion \u2014 the wizard shows a warning when the companion is missing. Install both plugins together.<\/p><\/dd>\n<dt id=\"does%20the%20plugin%20work%20with%20woocommerce%3F\"><h3>Does the plugin work with WooCommerce?<\/h3><\/dt>\n<dd><p>Not directly. The WooCommerce TWINT gateway is packaged in the paid \u00ab Pay4All Pro \u00bb add-on, distributed from https:\/\/pay4all.ch\/. That add-on installs on top of this free plugin and wires the WooCommerce checkout, Checkout Blocks, HPOS compatibility and background monitoring.<\/p><\/dd>\n<dt id=\"where%20do%20i%20find%20my%20twint%20credentials%3F\"><h3>Where do I find my TWINT credentials?<\/h3><\/dt>\n<dd><p>Log in to https:\/\/portal.twint.ch, create a store and generate a certificate. The wizard asks for the store UID, certificate password, cash-register ID and the certificate file.<\/p><\/dd>\n<dt id=\"do%20i%20need%20a%20pay4all%20account%20or%20license%3F\"><h3>Do I need a Pay4All account or license?<\/h3><\/dt>\n<dd><p>No. This plugin is fully free and works standalone (with Pay4All Shop). You only need TWINT merchant credentials.<\/p><\/dd>\n<dt id=\"is%20wordpress%20multisite%20supported%3F\"><h3>Is WordPress Multisite supported?<\/h3><\/dt>\n<dd><p>Not in this version. Activate the plugin site by site.<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>1.3.0<\/h4>\n\n<ul>\n<li><strong>Security \u2014 merchant certificate no longer at a predictable URL.<\/strong> The TWINT PEM (private key + certificate) used to be written to <code>wp-content\/uploads\/t4a_twint\/twint.pem<\/code> \u2014 a fixed, guessable path protected only by <code>.htaccess deny from all<\/code>. On nginx (which silently ignores <code>.htaccess<\/code>) or Apache with <code>AllowOverride None<\/code>, the file was directly downloadable. Each merchant now gets a random 48-hex filename minted per install (stored in <code>t4a_twint_settings_certfile<\/code>), the containing folder ships both an <code>index.php<\/code> and an <code>.htaccess<\/code> (belt-and-suspenders), and the file is chmod'd to 0600. Certificate renewal via TWINT backoffice also mints a fresh name, so a previously exposed URL is invalidated.<\/li>\n<li><strong>Security \u2014 certificate password encrypted at rest.<\/strong> The password protecting the private key was stored in cleartext in <code>wp_options<\/code> and re-emitted in the wizard HTML + AJAX response + parsing error message. It is now stored encrypted via authenticated encryption (see below), never echoed back to the DOM (the field renders empty with a \u00ab laissez vide pour conserver \u00bb hint), and the \u00ab Failed to parse certificate \u00bb error no longer includes the provided password.<\/li>\n<li><strong>Security \u2014 authenticated encryption for stored secrets.<\/strong> <code>Cipher<\/code> was AES-256-CTR with a length-checked salt suffix \u2014 malleable, no MAC, key material was <code>LOGGED_IN_KEY<\/code> used raw. Replaced with <code>sodium_crypto_secretbox<\/code> (XChaCha20-Poly1305) with an <code>openssl aes-256-gcm<\/code> fallback, keyed via HKDF-SHA256 from <code>LOGGED_IN_KEY + LOGGED_IN_SALT<\/code>. Ciphertexts written by earlier versions keep decrypting (backward-compatible reader) so no data loss.<\/li>\n<li><strong>Security \u2014 TWINT return signature now expires.<\/strong> The signature on the redirect \/ cancel URL was computed with a fixed <code>t=1<\/code>, meaning it was replayable indefinitely. It now embeds the real timestamp and rejects any URL older than <code>T4A_REDIRECT_SIGNATURE_EXPIRATION<\/code> (bumped from 60 s to 30 min so realistic TWINT-app flows are not rejected).<\/li>\n<li><strong>Security \u2014 <code>FileServer<\/code> defense-in-depth.<\/strong> Internal <code>basename()<\/code> + type whitelist + <code>realpath<\/code> bounding are now enforced inside <code>send_file()<\/code> \/ <code>display_pdf()<\/code>, in addition to the callers' existing sanitisation. Any request that escapes the allowed upload directory returns HTTP 404.<\/li>\n<li><strong>Security \u2014 nginx-proof folder protection.<\/strong> <code>t4a_twint\/<\/code>, <code>logs\/<\/code>, <code>temp\/<\/code>, <code>locks\/<\/code> now systematically ship both <code>.htaccess<\/code> and an empty <code>index.php<\/code> so servers that ignore <code>.htaccess<\/code> still can't list or serve them.<\/li>\n<li>Internal : <code>EnvironmentConfig::get_certificate_file_path()<\/code> is resolved LIVE (not from the <code>T4A_CERTIFICATE_PATH<\/code> constant frozen at boot) so a cert upload followed by a connection test in the same request reads the just-written file. Same for the certificate password \u2014 the SoapClient reads it via <code>get_certificate_password()<\/code> instead of <code>stripcslashes(get_option(...))<\/code>.<\/li>\n<\/ul>\n\n<h4>1.2.0<\/h4>\n\n<ul>\n<li><strong>Wizard rework \u2014 new \u00ab Commande \u00bb tab.<\/strong> The setup wizard now has 4 steps (Pr\u00e9paration, Identifiants, Commande, \u00c9tat). The \u00ab Commande \u00bb tab merges the Pay4All Shop and WooCommerce post-payment order-status configuration into a single unified card, with matching labels (\u00ab Commande contenant\u2026 \u00bb) between both integrations and editable status pickers.<\/li>\n<li><strong>Standalone boot.<\/strong> The plugin no longer requires Pay4All Shop to be present at activation. When the companion is missing, a scoped admin notice explains what's needed instead of blocking activation. WooCommerce-only merchants can install Pay4All Pro next to this plugin and skip Pay4All Shop entirely.<\/li>\n<li><strong>Pay4All Pro compatibility.<\/strong> The Commande tab detects when Pay4All Pro is active and offers a \u00ab Plus de r\u00e9glages \u00bb shortcut to <code>admin.php?page=pay4all-twint-woo<\/code>, keeping advanced WooCommerce settings in a single place.<\/li>\n<li><strong>Localized tutorial link.<\/strong> The \u00ab Besoin d'aide \u00bb block in the Pr\u00e9paration tab now points to <code>https:\/\/pay4all.ch\/p\/twint-woocommerce#tutorials<\/code> in FR\/DE\/IT\/EN.<\/li>\n<li>Internal : FormDomainEvents.mark_payment_paid delegates to a shared <code>paid_status_for_p4all_form_order()<\/code> helper so Pay4All Shop and WooCommerce share the same virtual\/deferred\/default logic.<\/li>\n<\/ul>\n\n<h4>1.1.0<\/h4>\n\n<ul>\n<li><strong>SOAP client rewritten in non-WSDL mode.<\/strong> The full TWINT Merchant Service v8.4 WSDL\/XSD contract (previously shipped as <code>.xml<\/code> schema files under <code>src\/Engine\/Soap\/schemas\/v8_4\/<\/code>) has been removed from the plugin package per WordPress.org's blanket file-type policy. Every SOAP request is now built as a hand-written XML envelope inside <code>TwintSoapClient::dispatch()<\/code>, transported by <code>\\SoapClient<\/code> in non-WSDL mode (mTLS + gzip + timeouts unchanged), and the response is parsed by an in-house <code>DOMDocument<\/code> \u2192 <code>stdClass<\/code> converter that mimics SoapClient's classic <code>-&gt;_<\/code> shape so every existing consumer (<code>$response-&gt;Order-&gt;Status-&gt;Status-&gt;_<\/code>, etc.) keeps working. Validated live against production TWINT on a small-amount payment + refund round-trip (StartOrder \u2192 GetOrder polling \u2192 ConfirmOrder \u2192 REVERSAL).<\/li>\n<li><strong>Sensitive payloads no longer logged.<\/strong> The SOAP journal used to record the full <code>$args<\/code> request payload and <code>$response<\/code> object for every operation. On WordPress.org's review request the plugin now only logs the operation name + message id, never the payload. Applies to <code>TwintSoapClient<\/code>, <code>TransactionService<\/code>, <code>BackofficeTransactionService<\/code> and <code>AdminAjax::check_successful_order_statuses<\/code>. Debug-only <code>print_r( $this-&gt;client )<\/code> block removed.<\/li>\n<li>Internal : <code>set_headers()<\/code>, <code>send_soap_request()<\/code>, <code>get_default_params()<\/code> retired ; replaced by <code>dispatch()<\/code> + <code>build_envelope()<\/code> + <code>merchant_information_xml()<\/code> helpers.<\/li>\n<\/ul>\n\n<h4>1.0.0<\/h4>\n\n<ul>\n<li>Initial release on WordPress.org.<\/li>\n<li>TWINT integration for the free Pay4All Shop plugin.<\/li>\n<li>Setup wizard, certificate handling, cash-register enrollment.<\/li>\n<li>French, German, Italian and English translations bundled.<\/li>\n<\/ul>","raw_excerpt":"Adds the TWINT payment gateway to Pay4All Shop (order-form-based checkout). Free, no license, no domain activation.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/scn.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/337135","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/scn.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/scn.wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/scn.wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=337135"}],"author":[{"embeddable":true,"href":"https:\/\/scn.wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/pay4all"}],"wp:attachment":[{"href":"https:\/\/scn.wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=337135"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/scn.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=337135"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/scn.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=337135"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/scn.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=337135"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/scn.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=337135"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/scn.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=337135"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}