Datatrans Payments
More than 4,500 customers, 126 million transactions in the last year make Datatrans one of Europe’s leading payment service providers. Whether you have a big global project or are an online start-up, you can expect custom solutions and the reliability of a highly successful Swiss enterprise.
Merchant Requirements
Datatrans is available for merchants in the DACH region.
More information about the required transaction volume and pricing is available under the following link.
Limitations
Following the
announcement
from Datatrans about security and API changes, we’ve prepared the following instructions for your compliance. There are a few changes required in your Datatrans and Piano dashboards. For more details please see this
guide
.
-
This only impacts your Credit card and Postfinance methods - as these are using XML API and the security change requires you to enable HMAC signage. If you need more time to enable HMAC, you can reach out to your Datatrans rep to whitelist your app.
The Twint payment method requires a Twint voucher, which must be linked to a Swiss bank account and therefore supports only payments in CHF.
You cannot remove the postal code field from checkout for credit card payments as it's required by the payment provider itself.
Datatrans currently does not support Apple Pay in third-party browsers. To complete a purchase using Apple Pay, the user must be on a device with Apple Pay properly configured and must use Safari. When testing in other browsers, the Apple Pay button may not appear. For more details, see the Datatrans documentation.
Template Changes
Datatrans 3DS Billing Address Fields
Three new optional fields can be added to the Datatrans Credit Card checkout form to improve 3DS authentication data quality and increase the likelihood of frictionless authentication for your subscribers
New toggles in Datatrans Credit Card settings (Edit Business → Payment providers → Datatrans Credit Card):
-
Billing address (street) →
billAddrLine1 -
Billing city →
billAddrCity -
Billing country →
billAddrCountry
All default to disabled. ZIP code is a separate existing field and is not part of this update.
These fields apply to Checkout, My Account Wallet, and ATW / Subscribe User (initial transactions only; recurring renewals are not affected).
⚠️Important — template update required first
If you are using customized Payment Components templates, you must update your templates before enabling the toggles. Enabling the toggles without updating the template will cause validation errors that block purchases.
Step 1 — Update templates (required for customized templates)
-
Checkout: Add the below code for the billing address field rows inside after the existing card fields in the Payment Components template.
<tr ng-show="cc.collectBillingAddress && !input.billingAddressEnabled"> <td colspan="4" class="field-card-billing-address"> <div class="field-wrapper"> <label class="icon road"></label> <div class="input"> <input class="flat" type="text" name="billAddrLine1" required placeholder="{{'Billing address' | tc:'checkout.platform'}}" ng-model="credit_card.billAddrLine1"/> </div> </div> <span class="error-message" ng-if="ccForm.billAddrLine1.$dirty && ccForm.billAddrLine1.$invalid"> <t>Billing address is required.</t> </span> </td> </tr> <tr ng-show="cc.collectBillingCity && !input.billingAddressEnabled"> <td colspan="4" class="field-card-billing-city"> <div class="field-wrapper"> <label class="icon city"></label> <div class="input"> <input class="flat" type="text" name="billAddrCity" required placeholder="{{'Billing city' | tc:'checkout.platform'}}" ng-model="credit_card.billAddrCity"/> </div> </div> <span class="error-message" ng-if="ccForm.billAddrCity.$dirty && ccForm.billAddrCity.$invalid"> <t>Billing city is required.</t> </span> </td> </tr> <tr ng-if="cc.showBillingCountryDropdown"> <td colspan="4" class="field-card-billing-country"> <div class="field-wrapper"> <label class="icon country"></label> <div class="input"> <div class="tp-dropdown__select billing-country-drop-down" ng-class="{'placeholder-shown': !credit_card.billAddrCountry}" tp-country-selector selector-options="datatransBillingCountrySelectorOptions" default-country="datatransDefaultBillingCountry" click-on-select="selectDatatransBillingCountry" side="'bottom'" country-type="'datatransBillingCountry'" selector-label="{{'Billing country' | tc:'checkout.platform'}}" dropdown-styles="{width: 230, zIndex: 1004, margin: 0}" tabindex="0" role="combobox" aria-label="{{'Billing country' | tc:'checkout.platform'}}"> </div> </div> </div> </td> </tr> -
My Account Wallet: Update the My Account Components template as follows; locate the Datatrans card table and find the Cardholder name row (the whose input has
aria-label="Cardholder name"). Immediatelly after, insert the following:.<tr class="table-row" ng-if="datatransCollectBillingAddress && !fullBillingAddressEnabled"> <td class="table-cell" colspan="4"> <label class="icon billing-address" aria-hidden="true"></label> <input type="text" ng-model="cardset.billAddrLine1" placeholder="{{'Billing address' | tc:'checkout.platform'}}" name="billAddrLine1" ng-required="datatransCollectBillingAddress && !fullBillingAddressEnabled" ng-readonly="readonly === true" aria-label="{{'Billing address' | tc:'checkout.platform'}}" /> </td> </tr> <tr class="table-row" ng-if="datatransCollectBillingCity && !fullBillingAddressEnabled"> <td class="table-cell" colspan="4"> <label class="icon city" aria-hidden="true"></label> <input type="text" ng-model="cardset.billAddrCity" placeholder="{{'Billing city' | tc:'checkout.platform'}}" name="billAddrCity" ng-required="datatransCollectBillingCity && !fullBillingAddressEnabled" ng-readonly="readonly === true" aria-label="{{'Billing city' | tc:'checkout.platform'}}" /> </td> </tr> <tr class="table-row" ng-if="datatransCollectBillingCountry && !fullBillingAddressEnabled && !isTaxBillingCountryEnabled"> <td class="table-cell" colspan="4"> <label class="icon country" aria-hidden="true"></label> <div class="tp-dropdown__select billing-country-drop-down datatrans-billing-country" ng-class="{'placeholder-shown': !cardset.billAddrCountry}" tp-country-selector selector-options="datatransBillingCountrySelectorOptions" default-country="datatransDefaultBillingCountry" click-on-select="selectDatatransBillingCountry" side="'bottom'" country-type="'datatransBillingCountry'" selector-label="{{'Billing country' | tc:'checkout.platform'}}" dropdown-styles="{width: 230, zIndex: 1004, margin: 0}" tabindex="0" role="combobox" aria-label="{{'Billing country' | tc:'checkout.platform'}}"> </div> </td> </tr>
Step 2 — Enable toggles in Edit Business → Payment providers → Datatrans Credit Card settings
Country selector note: The billing country field uses Piano's standard country selector component (tp-country-selector). To pre-select a country (e.g. Switzerland or Germany), customize the component in your template. Localization is supported via standard Piano localization strings.
Postal code note: The postal code field is already shown and required by default, in case you previously disabled it in your customized template, re-enabling it alongside these new fields will provide the most complete billing address signal for 3DS.
Cardholder name
Before enabling the collection of the cardholder's name for card payments in the configuration, ensure that your system templates are updated accordingly.
There are numerous ways to update your system template to fetch the latest functionality:
-
Reset to default (easiest, but depending on your level of customization,n you may need to decide if feasible)
-
Use the Compare to default feature in the System template editor, and carry new lines of code over to your version
-
Follow the instructions below to implement the new lines of code in your system template
Go to Manage → Templates → System → Payment Components, and find the component ]]>
Google Pay
<script type="text/ng-template" id="/frontend/providers/components/datatransgooglepay/checkout/component.shtml">
<!-- https://paymentbutton.datatrans.dev/ -->
<div id="datatrans-google-pay-options" class="hidden"></div>
<div class="datatransgooglepay-wrapper new-card-form">
<div class="row">
<div class="googlepay-checkout-payment-form" ng-class="{'googlepay-checkout-payment-form--custom-indent': isIndentRequired}">
<!-- Stored Payment Screen -->
<div ng-show="isStoredPaymentsScreenShown()">
<div class="add-credit-card-wrapper">
<h4><t>Your Google Pay cards</t></h4>
<a href="javascript:void(0)" class="add-credit-card" ng-click="gotoNewPaymentScreen()" ng-show="stored.length > 0">
+ <t>Add a new card</t>
</a>
</div>
<table ng-if="stored.length > 0" class="googlepay-table">
<tr class="googlepay-table-row" ng-repeat="upi in stored">
<td class="googlepay-table-cell googlepay-table-cell--radio">
<input
type="radio"
id="radio-{{upi.id}}"
name="stored_method"
ng-model="data.selectedUpi"
ng-change="onUpiChange(upi)"
ng-value="upi"
class="googlepay-radio"
/>
</td>
<td class="googlepay-table-cell googlepay-table-cell--short-logo">
<div class="datatrans-gpay-short-logo"></div>
</td>
<td class="googlepay-table-cell">
<label for="radio-{{upi.id}}">
<t context="checkout.platform">{{upi.display_name}}</t>
</label>
</td>
<td class="googlepay-table-cell payment-method-card-cell-expiration">
<t>Expires in</t> {{upi.expire_month}}/{{upi.expire_year}}
</td>
<td class="payment-method-card-cell payment-method-card-cell--default-payment">
<div class="payment-method-card-default-payment" ng-show="upi.id === data.defaultPaymentMethodId">
<t>Default</t>
</div>
<button
ng-hide="upi.id === data.defaultPaymentMethodId"
ng-click="setAsDefault(upi)"
type="button"
class="set-as-default-button"
>
<t>Set as default</t>
</button>
</td>
</tr>
</table>
</div>
<!-- New Payment Screen -->
<div ng-show="isNewPaymentScreenShown()" class="googlepay-add-new-account">
<div desktop class="go-back-to-cards-wrapper">
<a href="javascript:void(0)" class="go-back-to-cards" ng-click="gotoStoredPaymentsScreen()" ng-show="isReturnToStoredPaymentsShown()">
<t>Return to saved cards</t>
</a>
<h4><t>Add a new Google Pay card</t></h4>
</div>
<div ng-if="!data.newUpi" class="datatrans-gpay-account-wrapper">
<div class="hint">
<div class="googleplay-hint-container">
<t>
Add Google Pay as a payment account to pay for your purchases and take the protection of Datatrans with you
</t>
</div>
</div>
<div class="gpay-container-wrapper">
<div ng-show="isDtGpayLoading" id="dt-gpay-spinner-wrapper" class="spinner-wrapper">
<div pn-spinner size="small"></div>
</div>
<div
ng-show="!isDtGpayLoading"
id="googlepay-pay-button"
class="pm-login-button googlepay-login-button-wrapper"
data-e2e="googlepay-login-button">
</div>
</div>
</div>
<table ng-if="data.newUpi" class="googlepay-table">
<tr class="googlepay-table-row">
<td class="googlepay-table-cell googlepay-table-cell--radio">
<input
type="radio"
id="gpay-new-payment-method"
class="googlepay-radio"
name="stored_method"
ng-model="data.selectedUpi"
ng-value="data.newUpi"
/>
</td>
<td class="googlepay-table-cell googlepay-table-cell--short-logo">
<div class="datatrans-gpay-short-logo"></div>
</td>
<td class="googlepay-table-cell">
<label for="gpay-new-payment-method">
<t context="checkout.platform">{{data.newUpi.display_name}}</t>
</label>
</td>
<td class="googlepay-table-cell">
<div class="googlepay-logged-in">
<div ng-click="cancelAndInitNewAccount()" class="googlepay-cancel-button">
<t>Cancel</t>
</div>
</div>
</td>
</tr>
</table>
</div>
</div>
<!-- Manage Payment Method -->
<div class="manage-payment-method-wrapper" ng-show="state.payWithNew">
<div ng-if="!isSubscription() && isPaymentTerm()">
<div class="custom-checkbox">
<input type="checkbox" name="store_in_vault" ng-model="data.store_in_vault" id="remember-my-card-datatrans-gpay" />
<label for="remember-my-card-datatrans-gpay">
<t>Remember my card for future payments</t>
</label>
</div>
</div>
<div ng-if="isSubscription()">
<input type="hidden" name="store_in_vault" ng-model="data.store_in_vault" />
</div>
<div class="custom-checkbox" ng-if="data.store_in_vault">
<input type="checkbox" name="defaultPaymentMethodId" ng-model="data.defaultPaymentMethodId" id="default-payment-method-cybersource" />
<label for="default-payment-method-cybersource">
<t>Apply this payment method to all active subscriptions</t>
</label>
</div>
</div>
<!-- Consents and Footer -->
<div consents-list></div>
<div class="footer-modal" ng-show="canContinue()">
<div class="auto-renew-purchase" ng-show="isSelectedTermAutoRenewChoiceAvailable()">
<input class="auto-renew-purchase-checkbox" type="checkbox" ng-model="input.autoRenew" id="auto_renew" />
<label class="auto-renew-purchase-label" for="auto_renew">
<t>Automatically renew my subscription when the time comes.</t>
</label>
</div>
<div ng-show="isConfirmStepEnabled()" class="complete-purchase-button" data-e2e="googlepay-complete-purchase-button" goto-confirmation-button title="{{'Complete Purchase' | t}}"></div>
<div ng-show="!isConfirmStepEnabled()" class="complete-purchase-button" data-e2e="googlepay-complete-purchase-button" complete-purchase-button title="{{'Complete Purchase' | t}}"></div>
</div>
</div>
</div>
</script>
PayPal
<script type="text/ng-template" id="/frontend/providers/components/datatranspaypal/checkout/component.shtml">
<div class="datatranspaypal-wrapper new-card-form">
<div class="row">
<div ng-show="isStoredPaymentsScreenShown()" class="paypal-checkout-payment-form">
<div class="add-credit-card-wrapper">
<h4>
<t context="checkout.platform">Your PayPal accounts</t>
</h4>
<a href="javascript:void(0)" class="add-credit-card" ng-click="gotoNewPaymentScreen()"
ng-show="stored.length > 0">
+
<t>Add a new account</t>
</a>
</div>
<table ng-if="stored.length" class="paypal-table">
<tr class="paypal-table-row" ng-repeat="upi in stored">
<td class="paypal-table-cell--radio">
<input type="radio"
id="radio-{{upi.id}}"
name="stored_method"
ng-model="data.selectedUpi"
ng-value="upi"
ng-change="onUpiChange(upi)"
radio-track-by="id"
class="paypal-radio"/>
</td>
<td class="paypal-table-cell-logo">
<div class="datatrans-paypal-short-logo"></div>
</td>
<td class="paypal-table-cell-email">
<label for="radio-{{upi.id}}">
{{upi.email}}
</label>
</td>
<td class="payment-method-card-cell payment-method-card-cell--default-payment">
<div class="payment-method-card-default-payment"
ng-show="upi.id === data.defaultPaymentMethodId">
<t>Default</t>
</div>
<button ng-hide="upi.id === data.defaultPaymentMethodId"
ng-click="setAsDefault(upi)"
type="button"
class="set-as-default-button"
>
<t>Set as default</t>
</button>
</td>
</tr>
</table>
</div>
<div ng-show="isNewPaymentScreenShown()" class="paypal-checkout-payment-form">
<div class="go-back-to-cards-wrapper">
<h4>
<t>Add a new PayPal account</t>
</h4>
<a href="javascript:void(0)" class="go-back-to-cards" ng-click="gotoStoredPaymentsScreen()"
ng-show="isReturnToStoredPaymentsShown()">
<t>Return to saved accounts</t>
</a>
</div>
<div ng-if="!data.newUpi" class="datatrans-paypal-account-wrapper">
<div class="hint">
<t>Please, log in first.</t>
</div>
<div id="paypal-pay-button"
class="pm-login-button paypal-login-button"
ng-click="startWithNewAccount()"
data-e2e="paypal-login-button">
</div>
</div>
<table ng-if="data.newUpi" class="paypal-table">
<tr class="paypal-table-row">
<td class="paypal-table-cell--radio">
<input type="radio"
id="paypal-new-payment-method"
name="stored_method"
ng-model="data.selectedUpi"
ng-value="data.newUpi"
radio-track-by="id"
class="paypal-radio"/>
</td>
<td class="paypal-table-cell-logo">
<div class="datatrans-paypal-short-logo"></div>
</td>
<td class="paypal-table-cell-email">
<label for="paypal-new-payment-method">
{{data.newUpi.email}}
</label>
</td>
<td class="paypal-table-cell-cancel">
<div class="paypal-logged-in">
<div ng-click="cancelNewAccount()" class="paypal-cancel-button">
<t>Cancel</t>
</div>
</div>
</td>
</tr>
</table>
</div>
<div class="manage-payment-method-wrapper" ng-show="state.payWithNew">
<div ng-if="!isSubscription() && isPaymentTerm()">
<div class="custom-checkbox">
<input type="checkbox" name="store_in_vault" ng-model="data.store_in_vault"
id="remember-my-account-datatrans-paypal"/>
<label for="remember-my-account-datatrans-paypal">
<t>Remember my account for future payments</t>
</label>
</div>
</div>
<div ng-if="isSubscription()">
<input type="hidden" name="store_in_vault" ng-model="data.store_in_vault"/>
</div>
<div class="custom-checkbox" ng-if="data.store_in_vault">
<input type="checkbox" name="defaultPaymentMethodId" ng-model="data.defaultPaymentMethodId"
id="default-payment-method-datatrans-paypal"/>
<label for="default-payment-method-datatrans-paypal">
<t>Apply this payment method to all active subscriptions</t>
</label>
</div>
</div>
<div consents-list></div>
<div class="footer-modal" ng-show="canContinue()">
<div class="auto-renew-purchase" ng-show="isSelectedTermAutoRenewChoiceAvailable()">
<input class="auto-renew-purchase-checkbox" type="checkbox" ng-model="input.autoRenew"
id="auto_renew"/>
<label class="auto-renew-purchase-label" for="auto_renew">
<t>Automatically renew my subscription when the time comes.</t>
</label>
</div>
<div ng-show="isConfirmStepEnabled()" class="complete-purchase-button"
data-e2e="paypal-complete-purchase-button" goto-confirmation-button
title="{{'Complete Purchase' | t}}"></div>
<div ng-show="!isConfirmStepEnabled()" class="complete-purchase-button"
data-e2e="paypal-complete-purchase-button" complete-purchase-button
title="{{'Complete Purchase' | t}}"></div>
</div>
</div>
</div>
</script>
Twint
Go to Manage → Templates → System → Payment Components, find