Tamaro consists of several parts, which reside on different repositories:
- Tamaro Core
- Tamaro customer configurations
- Tamaro CLI
This document describes Tamaro Core configuration options and features.
Hence, Tamaro is quite complex and flexible, below is a brief description of the various concepts that are being used for Tamaro configuration.
Conditionals allow us to write configuration that changes based on various factors. For example, if payment methods is VISA, show the card block. To achieve something like that, you have to write a conditional. There are many different config keys that support conditionals. A conditional can either be a value of any kind - a simple value without a condition, or the same value wrapped into a if, then
structure – a value inside a condition.
Amounts without a condition:
# config.yml
amounts:
- 5
- 10
- 50
- 100
Amounts with a condition:
# config.yml
amounts:
- if: currency() == chf
then:
- 5
- 10
- 50
- 100
- if: currency() == usd
then:
- 10
- 20
- 100
- 200
In the example above, we want different amounts to be displayed depending on chosen currency.
A condition usually consists of operators and methods. The >
token is an operator.
There are many operators that you're already used to and some that might be new to you.
New operators can easily be added to the "Parser" class.
Besides operators, there are methods. The currency()
expression is a method.
Methods are simple functions that may, or may not, take arguments and return a value.
New methods can easily be added like this:
window.rnw.tamaro.instance.parser.addMethods({
date: () => new Date().toLocaleString()
})
You might write conditions either in traditional or functional format, for example,
traditional: 1 < 2 and 3 > 2
,
functional: and(lt(1, 2), gt(3, 2))
.
List of available operators:
Operator | Description |
---|---|
and, && | Logical and operator |
or, || | Logical or operator |
> | Greater then comparison |
>= | Greater then or equal to comparison |
< | Lesser then comparison |
<= | Lesser then or equal to comparison |
== | Equal to comparison |
=== | Strict equal to comparison |
!= | Not equal to comparison |
!== | Strict not equal to comparison |
+, -, *, / | Mathematical operators |
% | Modulo operator |
!, not, unless | Inversion operator |
in | Check if in array |
nin | Check if not in array |
List of available methods:
Method | Example | Description |
---|---|---|
and | and(true, true) |
Logical and comparison |
or | or(true, false) |
Logical or comparison |
gt | gt(2, 1) |
Greater then comparison |
gte | gte(2, 2) |
Greater then equal to comparison |
lt | lt(1, 2) |
Lesser then equal to comparison |
lte | lte(2, 2) |
Lesser then equal to comparison |
eq | eq(1, 1) |
Equal to comparison |
eqs | eqs(1, '1') |
Strict equal to comparison |
neq | neq(1, 2) |
Not equal to comparison |
neqs | neqs(1, '1') |
Strict not equal to comparison |
add | add(1, 2) |
Mathematical + operator |
sub | sub(2, 1) |
Mathematical - operator |
mul | mul(2, 3) |
Mathematical * operator |
div | div(6, 2) |
Mathematical / operator |
mod | mod(10, 3) |
Mathematical % operator |
arr | arr(1, 2, 3) |
Create an array from inputs |
round | round(1.5) |
Regular rounding |
ceil | ceil(1.8) |
Round to next highest |
floor | floor(1.2) |
Round to next lowest |
nan | nan('1') |
Check that value is a number, not a string |
in | in(1, arr(1, 2, 3)) |
Check that value is part of array |
nin | in(4, arr(1, 2, 3)) |
Check that value is not part of array |
neg | neg(true) |
Negate a value to the opposite |
val | val(1) |
Return given value, used internally |
purpose | purpose() |
Get payment form purpose |
paymentType | paymentType() |
Get payment form payment type |
recurringInterval | recurringInterval() |
Get payment form payment interval |
currency | currency() |
Get payment form currency |
amount | amount() |
Get payment form amount |
feeAmount | feeAmount() |
Get payment form fee amount |
formattedAmount | formattedAmount() |
Get payment form formatted amount |
formattedTotalAmount | formattedTotalAmount() |
Get payment form formatted total amount |
formattedFeeAmount | formattedFeeAmount() |
Get payment form formatted fee amount |
paymentMethod | paymentMethod() |
Get payment form payment method |
isCard | isCard('vis') |
Is given payment method a card payment method |
paymentForm | paymentForm('stored_customer_firstname') |
Get payment form field value by name |
showPaymentAddress | showPaymentAddress() |
Returns true if payment address block is mandatory |
isPostFinanceIban | isPostFinanceIban() |
Returns true if iban, filled in payment form, is PostFinance iban |
trans | trans('blocks.payment_purposes.title') |
Get translation string by path |
config | config('epp.apiKey') |
Get raw config option value by option name (path) |
resolveConfig | resolveConfig('amounts') |
Get resolved config option values by option name (path) |
resolveConfigWithSingleResult | resolveConfigWithSingleResult('allowCustomAmount') |
Get first resolved config option values by option name (path) |
transactionInfo | transactionInfo('payment_method') |
Get transactionInfo field value by name |
subscriptionInfo | subscriptionInfo('payment_method') |
Get subscriptionInfo field value by name |
transactionFinalStatus | transactionFinalStatus() |
Get final status of transaction |
abTestVariant | abTestVariant() |
Get current variant of AB testing |
Note: This list might be outdated, you can find an up-to-date list of operators and methods in debug tools (console):
window.rnw.tamaro.instance.parser.methods
The full list of available events are:
beforeLoad
afterLoad
beforeCreate
afterCreate
beforeRender
afterRender
fetchPaymentDataStart
fetchPaymentDataEnd
purposeChanged
amountChanged
currencyChanged
paymentTypeChanged
recurringIntervalChanged
paymentMethodChanged
beforePaymentValidateAndSend
beforePaymentValidate
afterPaymentValidate
paymentValidateSuccess
paymentValidateError
beforePaymentSend
paymentComplete
subscriptionUpdateComplete
subscriptionCancelled
To subscribe to an event you should call subscribe method of that one, passing a callback function as below:
function callbackPurposeChanged(event) {
console.log('The purpose was changed!')
}
window.rnw.tamaro.events.purposeChanged.subscribe(callbackPurposeChanged)
Subscribe to all events:
for (let eventName of Object.keys(rnw.tamaro.events)) {
window.rnw.tamaro.events[eventName].subscribe((event) => {
console.log(event.eventName, event.data)
})
}
All event handlers receive event
object as an argument with eventName
and data
properties.
data
property is an object that contains widget instance API object under api
property for ell events except beforeLoad
, afterLoad
, beforeCreate
.
Calling subscribe
function will return new function that allows you to unsubscribe from this event.
Example:
let unsubscribe = window.rnw.tamaro.events.purposeChanged.subscribe(callbackPurposeChanged)
unsubscribe()
Slots is a concept used in Tamaro that allows views to be extended with additional components. A slot is basically a placeholder that can be filled with an arbitrary component. Slots are identified by names. Check out debug tools for additional slot debugging helpers.
Tamaro can be configured using different ways:
- during build process
- in runtime
If you want customizations to be included into the bundle, you need to apply them during build process.
For more information how to build widget custom configurations, please checkout documentation in Tamaro configurations repository.
In example below:
- configurations are applied from custom YAML file
- styles are overridden in separate
scss
file -
widgetPreloader
instance is exposed to window aswindow.rnw.tamaro
global variable
// widget.tsx
import {set} from 'lodash'
import {createWidgetPreloader} from 'lib/WidgetPreloader'
const widgetPreloader = createWidgetPreloader({
resolvers: {
config: () => import('./config.yml'),
styles: () => import('./styles/widget.scss'),
translations: (language) => import(`./translations/${language}.yml`),
},
})
// EXPOSE_VAR is set to 'rnw.tamaro' by default
set(window, process.env.EXPOSE_VAR!, widgetPreloader)
IMPORTANT: It's recommended to use snake case for configuration options in YAML files and use camel case in runtime configuration. For example:
- use
recurring_intervals
in YAML - use
recurringIntervals
in runtime.
You can specify config object during widget initialization:
<!--index.html-->
<div id="root"></div>
<script>
window.rnw.tamaro.runWidget('#root', {
debug: false,
language: 'de',
})
</script>
You can override config values later by using the widget instance. For example:
window.rnw.tamaro.instance.config.debug = true
window.rnw.tamaro.instance.config.language = 'en'
You can also extend the widget configuration in event handlers, using the extendConfig
helper method.
If the passed configuration option value is a plain object, it's merged with its target value. Otherwise (if the passed configuration option value is bool, string, number, array, etc.), its target value is overridden by passed value.
Example:
// widget.tsx
import {set} from 'lodash'
import {createWidgetPreloader} from 'lib/WidgetPreloader'
const widgetPreloader = createWidgetPreloader()
widgetPreloader.events.afterCreate.subscribe((event) => {
const api = event.data.api
api.extendConfig({
// bool, string and array values override its target values
debug: false,
language: 'de',
amounts: [5, 10, 20, 50],
purposes: ['p1', 'p2'],
paymentMethods: ['cc', 'pp'],
// plain object value is deeply merged with its target value
translations: {
de: {
purposes: {
p1: 'Purpose 1',
p2: 'Purpose 2',
},
},
},
})
})
// EXPOSE_VAR is set to 'rnw.tamaro' by default
set(window, process.env.EXPOSE_VAR!, widgetPreloader)
Since the config object is reactive, whenever a value changes, the widget will update its user interface if necessary.
It is fine to combine these configuration options. For example, you might want to use YAML to set translations and some defaults, use JavaScript to set some landing-page specific configuration, and use a runtime configuration as reaction to certain events. No matter what configuration formats you've used, the widget will merge them all together using the priority Runtime > Customer's YML > Tamaro Defaults and create a new configuration object based on it.
Available configuration options are:
Key | Type | Description |
---|---|---|
debug | boolean |
Enable debug tools |
debugErrorMessages | boolean |
Display sample error messages, useful to see where error slots have been placed |
debugSlots | boolean |
Display placeholders for slots, useful to see where the slots have been placed |
language | string |
Set widget language |
fallbackLanguage | string |
Set widget fallback language |
testMode | boolean |
Enable transaction test mode |
flow | boolean |
Flow to use (epp or epms) |
translations | TranslatorMappedResource |
Provide custom translations |
showBlocks | Partial<ShowBlocksConfig> |
Define which blocks should be displayed inside the payment widget |
forceShowBlocks | Partial<ShowBlocksConfig> |
Define which blocks must be displayed inside the payment widget |
blocksOrder | BlockName[] |
Define the order of blocks inside the payment widget |
showBlockHeaders | Partial<ShowBlocksConfig> |
Define which blocks should have the header |
paymentWidgetBlocks | ConfigCondition<BlockName>[] |
Define which blocks should be displayed inside the payment widget (deprecated) |
showFields | Partial<ShowFieldsConfig> |
Define which fields should be displayed inside the payment widget |
forceShowFields | Partial<ShowFieldsConfig> |
Define which fields must be displayed inside the payment widget |
onlySubmitVisibleStandardFields | boolean |
Only submit those standard fields that are visible |
paymentFormPrefill | Partial<PaymentFormData> |
Prefill payment form |
eppEnv |
prod or stage
|
Define which EPP environment should be used for EPP payments |
epp | EppConfig |
A set of EppConfig parameters to be used for EPP payments |
eppStage | EppConfig |
A set of EppConfig parameters to be used for EPP payments |
epmsEnv |
prod or stage
|
Define which EPMS environment should be used for EPMS payments |
epms | EpmsConfig |
A set of EpmsConfig parameters to be used for EPMS payments |
epmsStage | EpmsConfig |
A set of EpmsConfig parameters to be used for EPMS payments |
purposes | ConfigCondition<string>[] |
Set available purposes |
paymentTypes | ConfigCondition<PaymentType>[] |
Set available payment types |
recurringIntervals | ConfigCondition<RecurringInterval>[] |
Set available recurring interval names |
currencies | ConfigCondition<PaymentCurrency>[] |
Set available currencies |
amounts | ConfigCondition<number>[] |
Set available amounts |
paymentMethods | ConfigCondition<PaymentMethod>[] |
Set available payment methods |
allowedCardTypes | ConfigCondition<PaymentMethod>[] |
Set allowed card types |
priorityCardTypes | ConfigCondition<PaymentMethod>[] |
Set priority card types |
autoselectPurpose | boolean |
Autoselect purpose if none is selected |
autoselectPaymentType | boolean |
Autoselect payment type if none is selected |
autoselectAmount | boolean |
Autoselect amount if none is selected |
autoselectPaymentMethod | boolean |
Autoselect payment method if none is selected |
purposeDetails | PurposeDetailsConfig |
Data related to purposes |
amountSubunits | AmountSubunitsConfig |
Define multipliers for various currencies (api expects amounts in cents) |
allowCustomAmount |
boolean or ConfigCondition<boolean>[]
|
Allow custom amount to be set |
minimumCustomAmount |
number or ConfigCondition<number>[]
|
Define the minimum custom amount either manually or regarding certain conditions |
maximumCustomAmount |
number or ConfigCondition<number>[]
|
Define the maximum custom amount either manually or regarding certain condition |
paymentProvider | PaymentProvider |
Set payment provider |
allowedRecurringPaymentMethods | ConfigCondition<PaymentMethod>[] |
Set allowed recurring payment methods |
faqEntries | ConfigCondition<string> |
A list of helpful and most frequently asked questions with answers on them |
expandedFaqEntries | ConfigCondition<string> |
A list of helpful and most frequently asked questions with answers on them |
salutations | ConfigCondition<string>[] |
Set available salutations |
countries | ConfigCondition<string>[] |
Set available countries |
priorityCountries | ConfigCondition<string>[] |
Set priority countries |
paymentValidations | ValidationConstraints |
Define validations for the payment form |
uiBreakpoints | {[key: string]: number} |
Define css breakpoints |
uiInvalidElementsScope | number |
Define CSS selector to limit auto-scrolling and auto-focusing invalid fields |
uiScrollOffsetTop | number |
Define offset from top of the page while automatically scrolling |
uiScrollOffsetBottom | number |
Define offset from bottom of the page while automatically scrolling |
showSubmitButton |
boolean or ConfigCondition<boolean>[]
|
Show/Hide submit button on payment form |
showFooter |
boolean or ConfigCondition<boolean>[]
|
Show/Hide footer |
showTestModeBar |
boolean or ConfigCondition<boolean>[]
|
Show/Hide testMode bar |
showRetryPaymentButton |
boolean or ConfigCondition<boolean>[]
|
Show/Hide "Make another donation" button |
recurringIntervalsLayout |
RecurringIntervalsLayout or ConfigCondition<RecurringIntervalsLayout>[]
|
Set how recurring intervals section should look like |
allowSwissQrBillOrder |
boolean or ConfigCondition<boolean>[]
|
Show/Hide "Also send me QR Bill on paper" checkbox |
requireSwissQrBillReferenceNumber |
boolean or ConfigCondition<boolean>[]
|
Set reference number to be automatically fetched for Swiss QR Bill payment method |
analyticsEventTracking |
boolean or ConfigCondition<boolean>[]
|
Enabled/disabled analytics event tracking |
abTestVariants | string>[] |
Define AB test variants |
coverFeeFixed |
number or ConfigCondition<number>[]
|
Value (in current currency) that will be added to payment amount to cover fee |
coverFeePercentage |
number or ConfigCondition<number>[]
|
Percent of amount that will be added to payment amount to cover fee |
ddFormTemplate |
string or ConfigCondition<string>[]
|
URL address of Direct debit form template PDF file |
oneClickPayment |
boolean or ConfigCondition<boolean>[]
|
Create a payment source token for further charges without credit card details |
debugPciProxy |
boolean or ConfigCondition<boolean>[]
|
Enable/disable debug for communication with PCI Proxy iframes |
recaptchaKey | string |
Enable google reCAPTCHA |
slots | SlotsConfig |
Place custom components into various slots |
errorLogging | boolean |
Enable sending of error logs to the monitoring service |
updateFormDataOnRetryPayment |
boolean or ConfigCondition<boolean>[]
|
Enable form prefill on retry payment flow |
donationReceiptRequiresFields | boolean |
Enable/disable Tamaro rules when donation receipt checkbox is checked |
autogeneratedDonationReceiptEnabled | boolean |
If enabled, adds additional parameter into EPMS payment object |
organisationCountryCode |
string | undefined |
Organisation country code, which is used to apply different rules when donation receipt checkbox is checked |
This section lists the configuration option for the EPMS e-mail service that sends transactional e-mails. The configuration files for e-mails reside in the email-config directory of every Tamaro configuration.
The configuration is deployed with npx -y @raisenow/tamaro-cli deploy-email-config
both for prod
and stage
environments. When you run this command,
you will be prompted for both AWS profile and environment.
Make sure selected AWS profile has proper permissions to access the desired environment.
Deploying to stage
using prod
profile and vice versa will end up with the error in the console.
In case you don't have properly configured AWS SSO Enabled profiles, please read the article
in the Wiki describing the steps how to get AWS SSO Enabled Profiles
.
E-mails are sent over the service if it is set as a webhook endpoint for the organisation. In other words, organisations have to activate e-mails in the "Settings" section in their Hub account.
The organisation's name should be set as parameter in the file email-config/parameters.yaml. Optionally, you can specify the organisation's e-mail address.
It is also possible to define an address to which a blind copy of the e-mail is sent. The e-mail is also sent to the blind-copy address if no supporter e-mail address is specified.
The parameters can be set per language, with a fallback (all
).
all:
organisation_name: ICRC
organisation_email: info@icrc.org
blind_copy: transactions@icrc.org
de:
organisation_name: IKRK
To define the texts used in the e-mails body and subject, you can create files of the form templates/{lang}/{scenario}.hbs
or templates/{lang}/{scenario}.subject.hbs
, respectively.
There are the following scenarios available:
-
transaction
for successful one-time payments -
subscription-activated
for when a subscription becomes active -
subscription-cancelled
for when a subscription is cancelled -
subscription-charged
for successful charges of subscriptions
The templates support Markdown and Handlebars syntax. Parameters must be set in double curly brackets.
Parameter | Description | Examples |
---|---|---|
greeting |
A language-specific greeting that depends on available personal data and cultural standards | Dear Ms Doubtfire, Ciao Antonio, Monsieur |
amount |
Amount with ISO currency symbol and decimal digits | EUR 5.00, CHF 2.55 |
amount_friendly |
Amount with typographic symbol; decimal digits are only used for non-integer amounts | € 5, Fr. 2.55 |
payment_method |
The payment method. It can just be a translated string or can contain further details specific to the payment method. | Visa Credit 4242 42•• •••• 4242, prélèvement SEPA |
payment_method_preposition |
A language-specific preposition that is usually used with the payment method | with your, mittels |
organisation.name |
Localised name of the organisation | IKRK |
organisation.email |
Localised e-mail address of the organisation | info@icrc.org |
subscription.interval |
Interval of the subscription as adjective | weekly, hebdomadaire |
subscription.interval_adverb |
Interval of the subscription as adverbial | weekly, mensilmente, tous les trois mois |
subscription.next_charge |
Date of the next charge | August 1, 2022 |
subscription.is_charged_immediately |
Boolean whether the first charge of the subscription will be done within 24 hours | true, false |
subscription.cancel_url |
URL to cancel the subscription | |
subscription.edit_url |
URL to edit the subscription | |
custom_parameters.purpose_text |
Text of the payment/subscription purpose | Where it's needed the most |
supporter |
The supporter object in the EPMS ontology | |
additional_information |
This is an array of objects with further details of the supporter. It contains a description property and the actual content , both localised |
|
custom_parameters |
This is an object containing all custom parameters in the payment object | Where it's needed the most |
There is also a legacy e-mail service that covers EPP and EPMS. It is still in use, but what was never documented. The legacy e-mail service's configuration files are named email-parameters.yaml
and translations.{lang}.yaml
. The EPMS e-mail service can read certain information from the legacy configuration (like the organisation name, organisation e-mail, and blind-copy address), but not the e-mail texts.
When working locally or testing the widget, you may want to enable the debug mode:
# config.yml
debug: true
You can also enable debug mode by adding #widgetdebug
to the url.
This enables debug tools at the bottom of the widget with various useful helpers and inspection views.
Alternatively you may also add a query parameter ?widgetdebug
to achieve the same effect.
You must disable test mode in order to produce production payments:
# config.yml
test_mode: false
To facilitate debugging of data you can set these options:
# config.yml
debug_error_messages: true
debug_slots: true
Change widget language.
Default value is 'en'
.
# config.yml
language: de
This language will be used for case, which don't have translations.
Default value is 'en'
.
# config.yml
fallback_language: de
There are two flows available: EPP (legacy e-payment platform, flow: epp
) and EPMS (e-payment micro-services, flow: epms
). It defines which backend to use in case a payment method is supported on either of them. The default is EPP.
You can add custom fields to the payment object. In case the EPP flow is used, the common approach is to use field names with a stored_
or stored_customer_
prefix. You can still use the same naming convention with the EPMS flow, but be aware that fields will be automatically remapped according to the rules below.
To choose the correct custom field names for the EPMS flow, please check out RaiseNow's ontology.
stored_customer_birthdate
should have YYYY-MM-DD
format.
EPP | EPMS |
---|---|
stored_customer_uuid | supporter.uuid |
stored_is_company_donation | supporter.is_company_donation |
stored_customer_company | supporter.legal_entity |
stored_customer_salutation | supporter.salutation |
stored_customer_firstname | supporter.first_name |
stored_customer_lastname | supporter.last_name |
stored_customer_raw_name | supporter.raw_name |
stored_customer_phone | supporter.phone |
stored_customer_email | supporter.email |
stored_customer_email_permission | supporter.email_permission |
stored_customer_street | supporter.street |
stored_customer_street_number | supporter.house_number |
stored_customer_street2 | supporter.address_addendum |
stored_customer_zip_code | supporter.postal_code |
stored_customer_city | supporter.city |
stored_customer_state | supporter.region_level_1 |
stored_customer_country | supporter.country |
stored_customer_raw_address | supporter.raw_address |
stored_customer_birthdate | supporter.birth_year supporter.birth_month supporter.birth_day |
EPP | EPMS |
---|---|
stored_customer_pobox | supporter.metadata.post_office_box |
stored_customer_{fieldname} | supporter.metadata.{fieldname} |
EPP | EPMS |
---|---|
stored_customer_donation_receipt | raisenow.integration.donation_receipt_requested |
stored_customer_message | raisenow.integration.message |
Additionaly to supporter.email_permission
the following is also set:
EPP | EPMS |
---|---|
stored_customer_email_permission | supporter.raisenow_parameters.integration.opt_in.email |
These 4 fields are automatically injected by Tamaro, you cannot override them.
EPP | EPMS |
---|---|
stored_rnw_product_name | raisenow.product.name |
stored_rnw_product_version | raisenow.product.version |
stored_rnw_widget_uuid | raisenow.product.uuid |
stored_rnw_source_url | raisenow.product.source_url |
EPP | EPMS |
---|---|
stored_{fieldname} | custom_parameters.{fieldname} |
In order to provide translation customisations, simply add them in dedicated files under "translations" folder:
# translations/en.yml
purposes:
p1: Purpose 1
p2: Purpose 2
# translations/de.yml
purposes:
p1: Zweck 1
p2: Zweck 2
When providing translations trough JavaScript,
translations
node is inside the config
object and a language key has to be used:
<!--index.html-->
<div id="root"></div>
<script>
window.rnw.tamaro.runWidget('#root', {
debug: true,
translations: {
en: {
purposes: {
p1: 'Purpose 1',
p2: 'Purpose 2',
},
},
de: {
purposes: {
p1: 'Zweck 1',
p2: 'Zweck 2',
},
},
},
})
</script>
You can use conditions for all available translation keys if needed. Only the first one value, that matches the condition, will be returned.
# translations/en.yml
purposes:
p1:
- if: amount() == 5
then: Purpose 1 when amount is 5
- if: amount() == 10
then: Purpose 1 when amount is 10
- Default purpose 1
You can put any of the supported methods and expressions by the conditionals parser,
into a translation by using the %%
around the expression, for example:
# translations/en.yml
title: 'Selected %% totalAmount() %% %% currency() %%'
The translated expression above would result in something like Selected 5 chf
You can reference other translations like this:
# translations/en.yml
title: 'Translated key currencies.chf: %% trans(currencies.chf) %%'
This would result in Translated key currencies.chf: Swiss Francs
You can configure a list of blocks for payment widget. Specify payment widget blocks like this:
# config.yml
show_blocks:
payment_purposes: true
payment_amounts_and_intervals: true
payment_payment_methods: true
payment_profile: true
payment_address: true
There can be circumstances when Tamaro will ignore the value
specified in this config option for certain blocks because it's required for the
transaction to be successful. For example, file-based Sepa direct debits require
the address fields and if this payment method is selected, Tamaro will
automatically render "payment_address"
block with all required address fields,
skipping showBlocks.payment_address: false
config option value.
You can use forceShowBlocks
config option to overrule this default Tamaro behavior,
but in this case you MUST be sure to set all required fields by yourself.
You can reorder blocks.
The following option only sets the order, but doesn't show/hide them.
# config.yml
blocks_order:
- payment_purposes
- payment_amounts_and_intervals
- payment_payment_methods
- payment_profile
- payment_profile_short
- payment_address
- payment_email_permission
- payment_cover_fee
- payment_sepa_mandate_terms
You can specify whether block header should be shown/hidden:
# config.yml
show_block_headers:
payment_payment_methods: false
slot_info: false
The legacy configuration option payment_widget_blocks
is still supported, but deprecated.
Fields can be shown and hidden by specifying the following options (shown are the defaults):
# config.yml
show_fields:
stored_is_company_donation: false
stored_customer_company: false
stored_customer_salutation: true
stored_customer_firstname: true
stored_customer_lastname: true
stored_customer_raw_name: false
stored_customer_phone: false
stored_customer_email: true
stored_customer_birthdate: true
stored_customer_pan: false
stored_customer_email_permission: true
stored_customer_message: true
stored_customer_donation_receipt: true
stored_customer_street: true
stored_customer_street_number: true
stored_customer_street2: true
stored_customer_pobox: false
stored_customer_zip_code: true
stored_customer_city: true
stored_customer_country: true
stored_customer_state: true
stored_customer_raw_address: false
bic: false
You also can use conditions for these options. Example:
# config.yml
show_fields:
stored_customer_pobox:
- if: paymentForm(stored_customer_country) == CH
then: true
else: false
There can be circumstances when Tamaro will ignore the value
specified in this config option for certain fields because it's required for the
transaction to be successful. For example, file-based Sepa direct debits require
the address fields and if this payment method is selected, Tamaro will
automatically render "payment_address"
block with all required address fields,
skipping showFields.stored_customer_street: false
and similar config option values.
You can use forceShowFields
config option to overrule this default Tamaro behavior, but in this case
you MUST be sure to set all required fields by yourself.
The legacy configuration option show_{fieldname}
is still supported, but
deprecated.
If a field is hidden, then its validations are skipped.
Depending of different conditions, Tamaro automatically shows some blocks and fields,
ignoring the values specified in showBlocks
and showFields
config options
for these fields, and also makes them required.
This may be overruled by forceShowBlocks
, forceShowFields
and forceFaymentValidations
config options, but if you use these config options, you MUST be sure to set
all required fields by yourself.
-
If payment type is
recurring
:-
email
is required
-
-
If payment method is
card
:-
first_name
is required -
last_name
is required
-
-
If payment method is
sepa_dd
:-
email
is required -
first_name
is required -
last_name
is required - if IBAN country is not in EEA (or equals to "FR"):
-
street
is required -
zip_code
is required -
city
is required -
country
is required -
state
is required (if states are defined for selected country)
-
-
-
If
stored_email_permission
checkbox is checked:-
email
is required
-
-
If
stored_is_company_donation
checkbox is checked:-
stored_customer_company
is required
-
-
If donation receipt checkbox is checked and if
donationReceiptRequiresFields
config options is enabled (enabled by default):- If
organisationCountryCode
is"CH"
or"DE"
:-
first_name
is required -
last_name
is required -
street
is required -
street_number
is required -
zip_code
is required -
city
is required -
country
is required -
state
is required (if states are defined for selected country)
-
- If
organisationCountryCode
is"AT"
:-
first_name
is required -
last_name
is required -
birthdate
is required
-
- If
organisationCountryCode
is other or not specified:-
first_name
is required -
last_name
is required -
street
is required -
zip_code
is required -
city
is required -
country
is required -
state
is required (if states are defined for selected country)
-
- If
If the configuration option onlySubmitVisibleStandardFields
is set to true
,
then prefilled standard fields will only
be submitted if they are visible to the supporter.
Default value is false
.
# config.yml
only_submit_visible_standard_fields: true
You can prefill the payment form by setting the fields that you want to be prefilled trough config:
# config.yml
payment_form_prefill:
stored_customer_firstname: John
stored_customer_lastname: Doe
purpose: p2
payment_type: recurring
recurring_interval: monthly
currency: EUR
amount: 10
payment_method: card
You can define options separately for "stage"
and "prod"
EPP environments and specify
the environment itself using eppEnv
config option.
There is a section "Debug → EPP/EPMS configs", where you can:
- Switch
eppEnv
config option value for testing purposes. - Explore resolved EPP config (also accessible in the browser console as
rnw.tamaro.instance.eppConfig
)
Switching eppEnv
config option value:
- If set to
"stage"
:-
eppStage
config option is used to resolveapiKey
,merchantId
,successUrl
,errorUrl
,cancelUrl
andimmediateExecution
. -
testMode
forcibly resolves astrue
.
-
- If set to
"prod"
:-
epp
config option is used to resolveapiKey
,merchantId
,successUrl
,errorUrl
,cancelUrl
andimmediateExecution
. -
testMode
resolves as value specified in the root level of config (eithertrue
orfalse
).
-
It's possible to use conditions for all EPP properties in epp
and eppStage
config options:
apiKey
, merchantId
, successUrl
, errorUrl
, cancelUrl
and immediateExecution
.
Please note that snake case is used
in config.yml
file instead of camel case
for config options names and their properties.
It's technically possible to use both cases, but it's highly recommended to not mix
them and stick to the snake case in YML config files for consistency and readability.
# config.yml
test_mode: false
epp_env: prod
epp:
api_key: epp-prod-api-key
merchant_id: epp-prod-merchant-id
success_url: https://example.com?success=1&prod=1
error_url: https://example.com?error=1&prod=1
cancel_url: https://example.com?cancel=1&prod=1
immediate_execution: false
epp_stage:
api_key:
- if: paymentMethod() == chqr
then: epp-stage-api-key-1
else: epp-stage-api-key-2
merchant_id:
- if: paymentMethod() == chqr
then: epp-stage-merchant-id-1
else: epp-stage-merchant-id-2
success_url: https://example.com?success=1&stage=1
error_url: https://example.com?error=1&stage=1
cancel_url: https://example.com?cancel=1&stage=1
immediate_execution: false
Default values, if not explicitly specified, are:
# config.yml
epp_env: prod
epp:
api_key: 1234567890
merchant_id: srk-aced5e
immediate_execution: false
Properties successUrl
, errorUrl
and cancelUrl
are considered deprecated and not recommended to use.
Legacy epikConfig
configuration option is still supported for backwards compatibility,
but considered deprecated and not recommended to use.
You can define options separately for "stage"
and "prod"
EPMS environments and specify
the environment itself using epmsEnv
config option.
There is a section "Debug → EPP/EPMS configs", where you can:
- Switch
epmsEnv
config option value for testing purposes. - Explore resolved EPMS config (also accessible in the browser console as
rnw.tamaro.instance.epmsConfig
)
Switching epmsEnv
config option value:
- If set to
"stage"
:-
epmsStage
config option is used to resolveaccountUuid
,paymentMethodProfile
,stripePublicKey
andstripeAccount
. -
testMode
forcibly resolves astrue
.
-
- If set to
"prod"
:-
epms
config option is used to resolveaccountUuid
,paymentMethodProfile
,stripePublicKey
andstripeAccount
. -
testMode
resolves as value specified in the root level of config (eithertrue
orfalse
).
-
It's possible to use conditions:
- For EPMS properties in
epms
andepmsStage
config options:accountUuid
,stripePublicKey
,stripeAccount
. - For each specific
paymentMethodProfile
parameter inepms
andepmsStage
config options.
Please note that snake case is used
in config.yml
file instead of camel case
for config options names and their properties.
It's technically possible to use both cases, but it's highly recommended to not mix
them and stick to the snake case in YML config files for consistency and readability.
Example:
# config.yml
test_mode: false
epms_env: prod
epms:
account_uuid: epms-prod-account-uuid
payment_method_profile:
card: epms-prod-payment-method-profile-card
twint:
- if: testMode() == true
then: epms-prod-test-payment-method-profile-twint
else: epms-prod-live-payment-method-profile-twint
sepa_dd: epms-prod-payment-method-profile-sepa_dd
apple_pay: epms-prod-payment-method-profile-wallet
google_pay: epms-prod-payment-method-profile-wallet
stripe_public_key: epms-prod-stripe-public-key
stripe_account: epms-prod-stripe-account
epms_stage:
account_uuid: epms-stage-account-uuid
payment_method_profile:
card: epms-stage-payment-method-profile-card
twint: epms-stage-payment-method-profile-twint
sepa_dd: epms-stage-payment-method-profile-sepa_dd
apple_pay: epms-stage-payment-method-profile-wallet
google_pay: epms-stage-payment-method-profile-wallet
stripe_public_key: epms-stage-stripe-public-key
stripe_account: epms-stage-stripe-account
Parser helper testMode()
evaluates to the value specified in config on root level.
The stripePublicKey
is required for wallet payments,
as an alternative to using stripeAccount
parameter.
Default values, if not explicitly specified, are:
# config.yml
epms_env: stage
epms_stage:
account_uuid: d71de4f6-e466-40dc-84ce-c1ce20b29b08
Legacy epikConfig
configuration option is still supported for backwards compatibility,
but considered deprecated and not recommended to use.
You may define available purposes like this:
# config.yml
purposes:
- p1
- p2
Do not forget to add a translation for each purpose:
# translations/en.yml
purposes:
p1: Stop the whitewalkers
p2: Unite seven kingdoms
You can limit widget to certain payment methods, types and intervals:
# config.yml
payment_methods:
- card
- sms
- sepa_dd
- apple_pay
- google_pay
payment_types:
- onetime
- recurring
recurring_intervals:
- monthly
- quarterly
- semestral
- yearly
Don't forget that you can use json format also, of course if this format is more preferable for you.
<!--index.html-->
<div id="root"></div>
<script>
window.rnw.tamaro.runWidget('#root', {
paymentMethods: [
'card',
'sepa_dd',
{
if: 'amount() < 100',
then: 'sms',
},
],
paymentTypes: [
'onetime',
'recurring'
],
recurringIntervals: [
'weekly',
'monthly',
],
})
</script>
You can specify which card types are supported.
Default value is undefined
, meaning all card types are supported.
allowed_card_types:
- vis
- eca
- amx
You can specify which icons of card types should be shown first in a "Card" block header.
Default value is ['vis', 'eca', 'amx']
.
# config.yml
priority_card_types:
- vis
- eca
- amx
You can set a limit of certain currencies to widget:
# config.yml
currencies:
- chf
- usd
Specify available amounts.
# config.yml
amounts:
- 5
- 35
- 150
Autoselect first value if none is selected.
Default values are the following:
# config.yml
autoselect_purpose: true
autoselect_payment_type: true
autoselect_amount: true
autoselect_payment_method: false
Each purpose can contain additional parameters, that will be sent along with the form.
By default purpose_details
is not defined so stored_campaign_id
and stored_campaign_subid
will not be added to payment object.
# config.yml
purposes:
- p1
- p2
purpose_details:
p1:
stored_campaign_id: id1
stored_campaign_subid: subid1
p2:
stored_campaign_id: id2
stored_campaign_subid: subid2
Define multipliers for various currencies (api expects amounts in cents):
# config.yml
amount_subunits:
chf: 100
eur: 100
usd: 100
To set currency without subunits set amount_subunits
to 1
for this currency:
# config.yml
amount_subunits:
clp: 1
By default, custom amount is allowed, but you can configure to not display it or display it conditionally:
# config.yml
allow_custom_amount: false
or with condition:
# config.yml
allow_custom_amount:
- if: purpose() == p2
then: true
else: false
You can specify a minimum custom amount manually adding a value or define the most appropriate value based on certain conditions.
Default value is 1
.
# config.yml
minimum_custom_amount: 10
or with condition:
# config.yml
minimum_custom_amount:
- if: currency() == chf
then:
- if: paymentType() == onetime
then: 5
- if: paymentType() == recurring
then:
- if: recurringInterval() == monthly
then: 6
- if: recurringInterval() == quarterly
then: 7
- if: recurringInterval() == semestral
then: 8
- if: recurringInterval() == yearly
then: 9
You can specify a maximum custom amount manually adding a value or define the most appropriate value based on certain conditions.
Default value is undefined
(unset).
# config.yml
maximum_custom_amount: 3000
or with condition:
# config.yml
maximum_custom_amount:
- if: currency() == chf
then:
- if: paymentMethod() == cc
then: 500
- if: paymentMethod() == twi
then: 3000
Available values are: adyen
, datatrans
, ingcollect
, stripe
.
Default value is datatrans
.
If stripe
payment provider is specified, and the backend is EPP, then payment will be sent with stp
payment method instead of vis
, eca
, etc.
# config.yml
payment_provider: datatrans
Payment over digital wallets are only available on EPMS, and only over Stripe. You can use the same payment method profile for Apple and Google Pay.
You either have to set the stripeAccount
parameter in the EPMS config
to the Stripe account identifier (acct_…
, available in the EPMS configurator)
of the connected organisation, or the stripePublicKey
in the EPMS config
to the Stripe public key of the organisation (must be provided by the organisation).
The payment methods will only be rendered if the browser might support them. For example, you will only see Google Pay in the Google Chrome browser. Upon selecting the payment method, requests are made to Apple or Google, respectively, to definitely check whether the wallet payment is available. An error is shown to the user in case it's not, asking to select a different payment method.
Wallet payments only work over HTTPS with a valid SSL certificate, also locally.
If you want to test Google Pay payments locally, check the tamaro-cli
documentation
for how to set up your environment for HTTPS.
For most cases, it's easier if you deploy Tamaro for testing.
Apple Pay additionally requires the domain on which Tamaro is accessed to be registered
with the Stripe organisation (you cannot register localhost, so you cannot test it locally,
unless you set up a proxy like ngrok).
The prerequisite for the registration is that the domain association file
is available under the path .well-known/apple-developer-merchantid-domain-association
on the domain; ask the organisation to host it there.
If you configured the Stripe public key, the organisation does the domain registration itself
in the Stripe Dashboard.
If you have configured the Stripe account identifier,
connected organisations must not configure it there.
Instead, we at RaiseNow have to make an API call:
curl -L -X POST 'https://api.stripe.com/v1/apple_pay/domains' \
-H 'Stripe-Account: {stripe_account}' \
-H 'Authorization: Basic {auth}' \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'domain_name={domain}'
with {stripe_account}
being the Stripe account identifier of the connected organisation,
{auth}
the base-64-encoded Stripe live key of EPMS, and {domain}
the domain name
on which Tamaro is served.
You can explicitly specify which payment methods should be allowed if recurring
payment type is selected.
Please note that if some of specified methods does not support recurring payments, then it won't be shown.
# config.yml
allowed_recurring_payment_methods:
- card
- pp
To show answers on the most frequently asked questions just fill a list out with keys, that will be translated in a runtime.
# config.yml
faq_entries:
- f1
- f2
- f3
# translations/en.yml
faq_entries:
f1:
question: First question
answer_html: Answer to the first question
f2:
question: Second question
answer_html: Answer to the second question
f3:
question: Third question
answer_html: Answer to the third question
If you need to keep an answer shown (expanded) on a certain question, expanded_faq_entries
option can be used.
Just specify keys of faq entries which needed to be expanded.
# config.yml
expanded_faq_entries:
- f1
By default, they are ['ms', 'mr', 'none']
.
# config.yml
salutations:
- ms
- mr
- none
# translations/en.yml
payment_form:
salutations:
ms: Ms
mr: Mr
none: No salutation
Note: When a user selects the salutation none
or mx
, no salutation will be displayed in the payment result
You can set available countries by adding their ISO 3166-1 alpha-2 codes. Don't forget to add translations for them as in an example below:
# config.yml
countries:
- CH
- DE
- FR
- IT
# translations/en.yml
countries:
CH: Switzerland
DE: Germany
FR: France
IT: Italy
You can set which countries will be on top of the dropdown list.
By default, it's ['CH', 'DE', 'FR', 'IT']
.
All the rest of the countries are sorted alphabetically by their translated name in ascending order.
# config.yml
priority_countries:
- CH
- DE
- FR
- IT
- AT
- LI
Define validations for the payment form.
# config.yml
payment_validations:
stored_customer_firstname:
required: true
stored_customer_lastname:
required: true
stored_customer_email:
required: true
email: true
stored_customer_street:
if: showPaymentAddress()
required: true
Furthermore, validation of certain fields can be defined by conditions,
as an example above with stored_customer_street
,
which will be required only if the payment address is shown.
There is a list of validation rules, that you can use:
alpha
, numeric
, alpha_numeric
, format
, email
, accepted
, required
.
All errors come with default messages, which can be customised with your own ones:
# translations/en.yml
validation_errors:
default:
field_is_not_alpha: The field must consist of letters only
field_is_not_numeric: The field must consist of numbers only
field_is_not_alpha_numeric: The field must consist of letters and numbers only
field_has_wrong_format: The field has wrong format
field_is_not_email: The field must be a valid email
field_has_wrong_date_format: The field has the wrong date format
field_min_age_invalid: The minimum age is {{min}}
field_max_age_invalid: The maximum age is {{max}}
field_is_not_accepted: The field must be accepted
field_is_missing: The field must not be empty
field_is_invalid: This value is invalid
purpose:
field_is_missing: Choose a purpose
payment_type:
field_is_missing: Please choose a donation frequency
amount:
field_is_missing: Amount missing
field_is_invalid: Amount invalid
minimum_custom_amount: Minimum amount is {{min}}
maximum_custom_amount: Maximum amount is {{max}}
payment_method:
field_is_missing: Choose a payment method
field_is_invalid: Payment method invalid
wallet_is_not_configured: "{{payment_method_name}} is not properly configured. Select another payment method."
cardno:
field_is_missing: Please provide your card number
field_is_invalid: Card number invalid
invalid_credit_card_type: Card type not supported
cvv:
field_is_missing: Please provide your card’s security code
field_is_invalid: Invalid security code
exp:
field_is_missing: Please provide an expiration date
field_is_invalid: Expiration date invalid
iban:
field_is_missing: Please provide your IBAN
field_is_invalid: This IBAN is invalid
field_is_unsupported: This IBAN cannot be used
bic:
field_is_missing: Please provide your BIC
field_is_invalid: This BIC is invalid
field_does_not_match_iban: This BIC does not match the above IBAN
bank_name:
field_is_missing: Please provide your bank name
sepa_mandate_terms:
field_is_not_accepted: You must accept these terms for SEPA Direct Debit. Alternatively, choose a different payment method.
msisdn:
field_is_missing: Please enter a correct Swiss mobile number
field_is_invalid: Please enter a correct Swiss mobile number
netbanking_issuer_id:
field_is_missing: Please select a bank
stored_customer_company:
field_is_missing: Please enter a company name
stored_customer_salutation:
field_is_missing: Please select a salutation
stored_customer_firstname:
field_is_missing: Please enter a first name
stored_customer_lastname:
field_is_missing: Please enter a last name
stored_customer_phone:
field_is_missing: Please enter a phone number
stored_customer_email:
field_is_missing: Please enter your email address
field_is_not_email: Please enter a valid e-mail address
stored_customer_birthdate:
field_is_missing: Please enter your date of birth
field_has_wrong_date_format: Date has the wrong format. Allowed format is "{{display_format}}".
field_min_age_invalid: Minimum age is {{min}}
field_max_age_invalid: Maximum age is {{max}}
stored_customer_pan:
field_is_missing: This field is required, please enter a valid Permanent Account Number (PAN)
field_has_wrong_format: Please enter a valid Permanent Account Number (PAN)
stored_customer_street:
field_is_missing: Please enter a street name
stored_customer_street_number:
field_is_missing: Please enter a house number
stored_customer_zip_code:
field_is_missing: Please enter a post code
field_is_invalid: Zip code is invalid
stored_customer_city:
field_is_missing: Please enter a town/city
stored_customer_country:
field_is_missing: Please select a country
stored_customer_state:
field_is_missing: Please make a selection
stored_customer_raw_name:
field_is_missing: Please enter a name
stored_customer_raw_address:
field_is_missing: Please enter the address
// widget.tsx
import {createWidgetPreloader} from 'lib/WidgetPreloader'
const widgetPreloader = createWidgetPreloader()
const checkEmail = async (value: string) => {
// make a call to some service to check if email exists
const response = await fetch('https://httpbin.org/anything', {
method: 'post',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
email: value,
}),
})
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const data = await response.json()
// check data and return either true or false
return value === '1@1.com'
}
widgetPreloader.events.afterCreate.subscribe(async (event) => {
const api = event.data.api
// Add custom validation rule
api.validator['addRule']('check_email_exists', async ({value}: any) => {
if (value) {
const emailExists = await checkEmail(value)
if (!emailExists) {
return {
code: 'email_does_not_exist',
message: 'E-mail address does not exist (default message)',
}
}
}
})
api.extendConfig({
// Add additional validation rule to "stored_customer_email" field
paymentValidations: {
stored_customer_email: {
check_email_exists: true,
},
},
// Add custom error message translation
translations: {
en: {
validation_errors: {
stored_customer_email: {
email_does_not_exist: 'E-mail address does not exist (message from JS)',
},
},
},
},
})
})
set(window, process.env.EXPOSE_VAR!, widgetPreloader)
uiBreakpoints
defines css breakpoints in rem
. Key is a class name. Values is the width of container.
When .tamaro-widget
container width is wider than specified value, then appropriate class name are added to this container.
# config.yml
ui_breakpoints:
tamaro-bp-xs: 28
tamaro-bp-sm: 33
The values above is default ones, but you can add additional breakpoints if needed. Example of their usage in css:
// styles/widget.scss
#tamaro-widget.tamaro-widget {
// default styles (mobile first)
}
#tamaro-widget.tamaro-widget.tamaro-bp-xs {
// styles that applies to container if its width > 28rem
}
#tamaro-widget.tamaro-widget.tamaro-bp-sm {
// styles that applies to container if its width > 33rem
}
uiInvalidElementsScope
config option allows you to specify a CSS selector
of the DOM element, within which scrolling to top invalid element and focusing
of top invalid element after form is submitted with invalid fields is performed.
-
Default value is
"#tamaro-widget"
– auto-scrolling and auto-focusing is scoped to Tamaro widget container. -
If Tamaro is rendered within a custom payment/donation form with for example ID attribute
"custom-form"
, you may specify:uiInvalidElementsScope: "#custom-form"
.This will allow to auto-scroll and auto-focus the fields, which are outside the Tamaro container.
-
If you want to entirely disable Tamaro's behavior of auto-scrolling and auto-focusing of invalid fields, you can specify:
uiInvalidElementsScope: undefined
.
Exampe:
# config.yml
ui_invalid_elements_scope: "#custom-form"
uiScrollOffsetTop
and uiScrollOffsetBottom
define offsets from top and bottom of the page
(or scrollable container in case Tamaro widget is rendered inside such container),
which will be used during automatic scrolling, in pixels.
For example, page automatically scrolls to the first invalid field (or block) after form validation.
If page has sticky header and/or footer, then they may overlap the validation error message,
so you can increase the value of these options in such cases to make validation error message visible.
The default value is 20
px for both options.
# config.yml
ui_scroll_offset_top: 200
ui_scroll_offset_bottom: 100
uiHeadingLevel
defines the level of heading tags.
Possible values: from1
to 6
.
According to this option, headings will be rendered as <h1>
- <h6>
tags respectively.
The default value is 2
.
# config.yml
ui_heading_level: 2
Option to show/hide submit button on payment form.
Default value is true
.
# config.yml
show_submit_button: true
Option to show/hide footer.
Default value is true
.
# config.yml
show_footer: true
TestMode bar is shown by default if testMode
configuration option is set to true
.
It can be forced to be hidden by using showTestModeBar: false
configuration option.
# config.yml
show_test_mode_bar: true
When payment is completed, then the link (button) "Make another donation" is shown by default.
This option can be used to hide this button.
# config.yml
show_retry_payment_button: true
recurringIntervalsLayout
changes the layout of the recurring intervals section.
Possible values are: "compact"
, "list"
.
Default value is "compact"
.
If set to "list"
, all defined recurring interval options will be displayed
directly in the form.
If set to "compact"
only the default recurring interval option will be displayed,
as well as "onetime"
. The rest of the configured recurring intervals are displayed as
options of a select dropdown.
Layout automatically falls back to "list"
in the following cases:
- If both
"onetime"
and"recurring"
payment types are available and there is only 1 allowed recurring interval. - If only
"recurring"
payment type is available and there are only 2 possible recurring intervals.
# config.yml
recurring_intervals_layout: "list"
Option to show/hide "Also send me the QR bill on paper" checkbox for Swiss QR Bill payment method.
Default value is false
.
# config.yml
allow_swiss_qr_bill_order: false
Sets reference number to be automatically fetched for Swiss QR Bill payment method.
Default value is false
.
# config.yml
require_swiss_qr_bill_reference_number: false
Enable/disable analytics event tracking.
Default value is the following:
# config.yml
analytics_event_tracking:
- if: testMode() == true
then: false
else: true
Define different values for AB testing
# config.yml
ab_test_variants:
- testA
- testB
If test variants are defined you can use parser helper and conditions to specify different behavior:
# config.yml
cover_fee_fixed:
- if: abTestVariant() == testA
then: 0.3
- if: abTestVariant() == testB
then: 0.2
- 0
Test variant will be chosen randomly (and preserved for current user) and passed into transaction
For EPP
:
{
// ...
"stored_rnw_analytics_ab_test_variant": "testA"
}
For EPMS
:
{
// ...
"raisenow_parameters": {
// ...
"analytics": {
// ...
"ab_test_variant": "testA"
}
}
}
These options allow supporter to cover fees:
# config.yml
payment_widget_blocks:
# other blocks...
- payment_cover_fee
cover_fee_fixed:
- if: currency() == usd
then: 0.25
- if: currency() == eur
then: 0.2
- 0
cover_fee_percentage:
- 5
URL address of Direct debit form template PDF file.
# config.yml
dd_form_template:
- if: config(language) == en
then:
- if: isPostFinanceIban()
then: https://assets.raisenow.com/static/1234567890/LEMA_LSV_Template_2017_EN_PostFinance.pdf
else: https://assets.raisenow.com/static/1234567890/LEMA_LSV_Template_2017_EN_Bank.pdf
- if: config(language) == de
then:
- if: isPostFinanceIban()
then: https://assets.raisenow.com/static/1234567890/LEMA_LSV_Template_2017_DE_PostFinance.pdf
else: https://assets.raisenow.com/static/1234567890/LEMA_LSV_Template_2017_DE_Bank.pdf
- if: config(language) == fr
then:
- if: isPostFinanceIban()
then: https://assets.raisenow.com/static/1234567890/LEMA_LSV_Template_2017_FR_PostFinance.pdf
else: https://assets.raisenow.com/static/1234567890/LEMA_LSV_Template_2017_FR_Bank.pdf
- if: config(language) == it
then:
- if: isPostFinanceIban()
then: https://assets.raisenow.com/static/1234567890/LEMA_LSV_Template_2017_IT_PostFinance.pdf
else: https://assets.raisenow.com/static/1234567890/LEMA_LSV_Template_2017_IT_Bank.pdf
When set to true
, a payment source token will be created, which can be used for further charges without card details.
Sample configuration:
# config.yml
one_click_payment: true
When flow
config options is set to 'epms'
, PCI Proxy is used – cardno
and cvv
fields are rendered inside iframes.
This parameter is to enable/disable debug of communication with these iframes.
Default value is false
.
# config.yml
debug_pci_proxy: true
You can enable google reCAPTCHA simply by providing a recaptcha api key. You can create one here. Make sure you use reCAPTCHA v2 (invisible). To test that recaptcha actually works, simply open the widget inside an anonymous browser window and submit a new payment. This should be enough to make recaptcha nervous and kick in.
# config.yml
recaptcha_key: 6Lcpk30UAAAAAG1JkpphachApU9eFC49V2-JUf1g
You may use the 6Lcpk30UAAAAAG1JkpphachApU9eFC49V2-JUf1g
api key for
local testing – this key works on localhost
and 127.0.0.1
only.
Define a description for each amount
# translations/en.yml
amount_descriptions:
- if: amount() == 5
then: Your donation will give access to informal professional education
- if: amount() == 10
then: Your donation assure healthy nutrition for pre-school children
- if: amount() == custom_amount
then: Your Donation will help us do our important work
else: Your Donation will help us do our important work
Provide hidden fields trough URL
You can inject hidden fields trough the URL query like this:
URL like http://url.com?rnw-stored_pixel_id=1234
results in a hidden field stored_pixel_id
being added to the payment form.
Please note that you should use rnw-
prefix for field names.
You can limit supported cards types by specifying them under payment_methods
option in config:
# config.yml
payment_methods:
- amx
- din
- dis
- jcb
- mae
- eca
- vis
If you want to support all card types, just specify 'card'
meta payment method and don't include
any other card types to this list:
# config.yml
payment_methods:
- card
There are many different slots placed throughout the widget that you may use to extend it. To see where the slots are, go to debug tools and enable the "Debug slots" option.
Sample slot configuration:
# config.yml
slots:
# slot name
profile_form_start:
# dynamic components configurations
- component: field
# ...dynamic field 1 options
- component: field
# ...dynamic field 2 options
Besides the preconfigured slots, you can add your own slot wherever you want by prefixing the slot name with 'slot_'
.
Example of adding a dynamic slot to the payment widget after "Purposes" block:
# config.yml
payment_widget_blocks:
- payment_purposes
- slot_1 # references dynamic slot
- payment_payment_methods
payment_form_prefill:
stored_customer_email: null
slots:
# define dynamic slot
slot_1:
- component: block
children:
- component: block_header
title: block_title # translation key
- component: block_content
children:
- component: field
type: text
name: stored_customer_email
label: payment_form.stored_customer_email # translation key
Tracked errors provide the context to quickly find the reason why the issue occurred. By default, the error event is not sent to the monitoring service.
# config.yml
error_logging: true
After the payment is completed, supporter is redirected to a Result Page, where
"Make another donation" button is shown. Clicking this button leads to the redirect
to the payment form view, where the form is prefilled with the data from the previous payment object.
When config option updateFormDataOnRetryPayment
is set to false
, the form
will not be prefilled with the data from the previous payment object.
Default value: true
.
# config.yml
update_form_data_on_retry_payment: true
If donation receipt checkbox is checked, Tamaro applies its own rules
to make some of the fields mandatory. Config option donationReceiptRequiresFields
allows to disable this bahavior, but in general not recommended to be used.
Default value: true
.
# config.yml
donation_receipt_requires_fields: true
If autogeneratedDonationReceiptEnabled
config option is set to true
and if supporter checked donation receipt checkbox,
then additional parameter raisenow_parameters.integration.raisenow_donation_receipt_available
is added into EPMS payment object.
This config option can be used with custom widgets if organisation has uploaded their donation receipt templates in their Hub acccount.
Default value: false
.
autogenerated_donation_receipt_enabled: true
Config option organisationCountryCode
is used to apply different rules
when donation receipt checkbox is checked.
Default value: undefined
.
# config.yml
organisation_country_code: CH
As already mentioned, you can place dynamic components into slots, below is a summary of available components.
Everything in this widget is grouped inside blocks, you can add a new block, for example into a dynamic slot, using this component.
component: block
children: []
It makes sense to put this as the first child into a block component. This component simply renders a common block header.
component: block_header
title: translation_key # translation key for title
Place it right after the block_header inside a block component.
component: block_content
children: []
Allows to set html content.
component: content
text_html: translation_key
Field component is used to render various inputs.
Allowed values for type
property are:
'text'
'textarea'
'date'
'radio-group'
'radio-in-group'
'checkbox'
'checkbox-group'
'checkbox-in-group'
'select'
'select-salutations'
'select-countries'
'select-states'
You can define default values for custom fields inside a prefill block:
# config.yml
payment_form_prefill:
stored_new_field: some value
All fields, added using slots can be validated as usual form fields. Put them into the payment_validations section of the configuration file:
# config.yml
payment_validations:
stored_new_field:
required: true
Dynamic field of type 'text'
:
# config.yml
payment_form_prefill:
stored_customer_company_name: ''
slots:
profile_form_start:
- component: field
type: text # or textarea, date
name: stored_customer_company_name
label: payment_form.stored_customer_company_name # translation key
placeholder: payment_form.stored_customer_company_name # translation key (optional)
infoToggleTitle: some_key # translation key (optional)
infoToggleTextHTML: some_key # translation key (optional)
# translations/en.yml
payment_form:
stored_customer_company_name: Company name
You can create radio groups by combining the 'radio-group'
and 'radio-in-group'
components.
# config.yml
payment_form_prefill:
stored_customer_type: company
slots:
profile_form_start:
- component: field
type: radio-group
inline: false # display radios horizontally or vertically
name: stored_customer_type
label: payment_form.stored_customer_type # translation key
children:
- component: field
type: radio-in-group
name: stored_customer_type
value: private # radio value
text: payment_form.stored_customer_type_private # radio text (translation key)
# textHTML: payment_form.stored_customer_type_private_html # checkbox text html (translation key)
- component: field
type: radio-in-group
name: stored_customer_type
value: company # radio value
text: payment_form.stored_customer_type_company # radio text (translation key)
# textHTML: payment_form.stored_customer_type_company_html # checkbox text html (translation key)
# translations/en.yml
payment_form:
stored_customer_type: Customer type
stored_customer_type_private: Private
stored_customer_type_private_html: |
<span>Private</span>
stored_customer_type_company: Company
stored_customer_type_company_html: |
<span>Company</span>
Dynamic field of type 'checkbox'
:
# config.yml
payment_form_prefill:
stored_customer_accept_terms: false
slots:
profile_form_start:
- component: field
type: checkbox
name: stored_customer_accept_terms
text: payment_form.stored_customer_accept_terms # checkbox text (translation key)
# textHTML: payment_form.stored_customer_accept_terms_html # checkbox text html (translation key)
# infoFixedHTML: payment_form.stored_customer_accept_terms_info_html # checkbox fixed info html (translation key)
# infoExpandableHTML: payment_form.stored_customer_accept_terms_info_html # checkbox expandable info html (translation key)
# translations/en.yml
payment_form:
stored_customer_accept_terms: I accept terms and conditions
stored_customer_accept_terms_html: |
<span>I accept terms and conditions</span>
stored_customer_accept_terms_info_html: |
<span>Some additional info</span>
You can create checkbox groups by combining the 'checkbox-group'
and 'checkbox-in-group'
components.
# config.yml
payment_form_prefill:
stored_customer_inform_via: null
stored_customer_inform_via_email: false
stored_customer_inform_via_phone: false
slots:
profile_form_start:
- component: field
type: checkbox-group
inline: true # display checkboxes horizontally or vertically
name: stored_customer_inform_via
label: payment_form.stored_customer_inform_via # translation key
children:
- component: field
type: checkbox-in-group
name: stored_customer_inform_via_email
text: payment_form.stored_customer_inform_via_email # checkbox text (translation key)
# textHTML: payment_form.stored_customer_inform_via_email_html # checkbox text html (translation key)
# infoFixedHTML: payment_form.stored_customer_inform_via_email_info_html # checkbox fixed info html (translation key)
# infoExpandableHTML: payment_form.stored_customer_inform_via_email_info_html # checkbox expandable info html (translation key)
- component: field
type: checkbox-in-group
name: stored_customer_inform_via_phone
text: payment_form.stored_customer_inform_via_phone # checkbox text (translation key)
# textHTML: payment_form.stored_customer_inform_via_phone_html # checkbox text html (translation key)
# infoFixedHTML: payment_form.stored_customer_inform_via_phone_info_html # checkbox fixed info html (translation key)
# infoExpandableHTML: payment_form.stored_customer_inform_via_phone_info_html # checkbox expandable info html (translation key)
# translations/en.yml
payment_form:
stored_customer_inform_via: Please keep me informed
stored_customer_inform_via_email: on email
stored_customer_inform_via_email_html: |
<span>on email</span>
stored_customer_inform_via_email_info_html: |
<span>Some additional info</span>
stored_customer_inform_via_phone: on phone
stored_customer_inform_via_phone_html: |
<span>on phone</span>
stored_customer_inform_via_phone_info_html: |
<span>Some additional info</span>
Dynamic field of type 'select'
:
# config.yml
payment_form_prefill:
stored_customer_drop_donations: null
slots:
profile_form_start:
- component: field
type: select
name: stored_customer_drop_donations
label: payment_form.stored_customer_drop_donations # translation key
options:
- value: yes
label: payment_form.drop_donations_options.yes # translation key
- value: no
label: payment_form.drop_donations_options.no # translation key
# translations/en.yml
payment_form:
stored_customer_drop_donations: Do you want to drop your donations?
drop_donations_options:
yes: I want to drop my donations
no: I don't want to drop my donations
Dynamic field of types 'select-salutations'
, 'select-countries'
, 'select-states'
:
# config.yml
show_fields:
slot_1: true
slots:
slot_1:
- component: block
children:
- component: block_header
title: slot_1.title
- component: block_content
children:
- component: field
type: select-salutations
name: stored_customer_salutation
#label: payment_form.stored_customer_salutation
#placeholder: payment_form.stored_customer_salutation
- component: field
type: select-countries
name: stored_customer_country
#label: payment_form.stored_customer_country
#placeholder: payment_form.stored_customer_country
- component: field
type: select-states
name: stored_customer_state
#countryFieldName: stored_customer_country # (default value)
#label: payment_form.stored_customer_state
#placeholder: payment_form.stored_customer_state
# translations/en.yml
slot_1:
title: Custom slot 1
The example below shows how to use slots to add custom fields with validations and translations:
-
stored_customer_type
(radio buttons, required) -
stored_customer_company_name
(text, shown and required only ifstored_customer_type
is "company") -
stored_customer_drop_donations
(select, required) -
stored_customer_inform_via
(checkbox-group) -
stored_customer_inform_via_email
(checkbox-in-group) -
stored_customer_inform_via_phone
(checkbox-in-group) -
stored_customer_accept_cookies
(checkbox) -
stored_customer_accept_terms
(checkbox, required)
This example is implemented in example5-slots customer configuration.
# config.yml
payment_form_prefill:
stored_customer_type: null
stored_customer_company_name: null
stored_customer_drop_donations: null
stored_customer_inform_via_email: false
stored_customer_inform_via_phone: false
stored_customer_accept_cookies: false
stored_customer_accept_terms: false
slots:
profile_form_start:
- component: field
type: radio-group
# inline: true
name: stored_customer_type
label: payment_form.stored_customer_type
children:
- component: field
type: radio-in-group
name: stored_customer_type
text: payment_form.stored_customer_type_private
value: private
- component: field
type: radio-in-group
name: stored_customer_type
text: payment_form.stored_customer_type_company
value: company
- if: paymentForm(stored_customer_type) == company
component: field
type: text
name: stored_customer_company_name
label: payment_form.stored_customer_company_name
placeholder: payment_form.stored_customer_company_name
profile_form_end:
- component: field
type: select
name: stored_customer_drop_donations
label: payment_form.stored_customer_drop_donations
options:
- value: yes
label: payment_form.drop_donations_options.yes
- value: no
label: payment_form.drop_donations_options.no
- component: field
inline: true
type: checkbox-group
name: stored_customer_inform_via
label: payment_form.stored_customer_inform_via
children:
- component: field
type: checkbox-in-group
name: stored_customer_inform_via_email
text: payment_form.stored_customer_inform_via_email
- component: field
type: checkbox-in-group
name: stored_customer_inform_via_phone
text: payment_form.stored_customer_inform_via_phone
- component: field
type: checkbox
name: stored_customer_accept_cookies
textHTML: payment_form.stored_customer_accept_cookies_html
infoFixedHTML: payment_form.stored_customer_accept_cookies_info_html
- component: field
type: checkbox
name: stored_customer_accept_terms
#text: payment_form.stored_customer_accept_terms
textHTML: payment_form.stored_customer_accept_terms_html
infoExpandableHTML: payment_form.stored_customer_accept_terms_info_html
# translations/en.yml
payment_form:
please_select: Please select...
customer_type_private: Private
customer_type_company: Company
drop_donations_options:
yes: I want to drop my donations
no: I don't want to drop my donations
stored_customer_type: Customer type
stored_customer_company_name: Company name
stored_customer_drop_donations: Do you want to drop your donations?
stored_customer_inform_via: Please keep me informed
stored_customer_inform_via_email: on e-mail
stored_customer_inform_via_phone: on phone
stored_customer_accept_cookies_html: |
<span>
I accept <a class="link" href="https://example.com" target="_blank">cookie policy</a>
</span>
stored_customer_accept_cookies_info_html:
<p>
Some additional fixed info
</p>
stored_customer_accept_terms: I accept terms and conditions
stored_customer_accept_terms_html: |
<span>
I accept <a class="link" href="https://example.com" target="_blank">terms and conditions</a>
</span>
stored_customer_accept_terms_info_html: |
<p>
Some additional expandable info
</p>
validation_errors:
stored_customer_type:
field_is_missing: Please make a selection
stored_customer_company_name:
field_is_missing: Please enter company name
stored_customer_drop_donations:
field_is_missing: Please select a value
stored_customer_accept_terms:
field_is_not_accepted: Please accept terms and conditions
// widget.tsx
import {set} from 'lodash'
import {createWidgetPreloader} from 'lib/WidgetPreloader'
const widgetPreloader = createWidgetPreloader({
resolvers: {
config: () => import('./config.yml'),
},
})
widgetPreloader.events.afterCreate.subscribe(async (event) => {
const api = event.data.api
api.extendConfig({
// Add custom validations
paymentValidations: {
stored_customer_type: {required: true},
stored_customer_company_name: {
if: 'paymentForm(stored_customer_type) == company',
required: true,
},
stored_customer_drop_donations: {required: true},
stored_customer_accept_terms: {accepted: true},
},
})
})
set(window, process.env.EXPOSE_VAR!, widgetPreloader)
The example below shows how to configure redirect to custom Result page after the payment/subscription is done.
If you want to add some additional query parameters to the page (for example payment ID, status etc.), it should be handled a bit differently depending of which flow (EPP or EPMS) was used to make the payment and also depending of is it one-off payment or subscription.
The example below handles all 4 cases:
- EPP one-off payment
- EPP subscription
- EPMS one-off payment
- EPMS subscription
<!--index.html-->
<div id="root"></div>
<script>
window.rnw.tamaro.events.fetchPaymentDataEnd.subscribe(async (event) => {
// Important!
// Wrapping the code into Promise is needed here to prevent
// flickering of default Tamaro Result page.
// It's very important to call "resolve()" function at the end of this code block.
// If you miss it, Tamaro form will not be shown and you will see
// endless loading spinner instead.
return new Promise((resolve) => {
// Important!
// In the "fetchPaymentDataEnd" event handler take objects from "event.data" directly,
// not from "event.data.api" because at the time this event is triggered,
// the state in "event.data.api" is not updated yet.
// EPP
const transactionInfo = event.data.transactionInfo
const subscriptionInfo = event.data.subscriptionInfo
// EPMS
const epmsPaymentInfo = event.data.epmsPaymentInfo
const epmsSubscriptionInfo = event.data.epmsSubscriptionInfo
const epmsPaymentSource = event.data.epmsPaymentSource
const customResultPage = 'https://example.com?foo=bar'
const newUrl = new URL(customResultPage)
// Feel free do add any additional params to the URL if you need
// EPP payment (one-off)
if (transactionInfo && !subscriptionInfo) {
newUrl.searchParams.set('epp_payment_id', transactionInfo.epp_transaction_id)
newUrl.searchParams.set('epp_payment_status', transactionInfo.epayment_status)
window.location.href = newUrl.href
return
}
// EPP subscription
if (subscriptionInfo) {
newUrl.searchParams.set('epp_subscription_token', subscriptionInfo.subscription_token)
newUrl.searchParams.set('epp_subscription_status', subscriptionInfo.status)
window.location.href = newUrl.href
return
}
// EPMS payment (one-off)
if (epmsPaymentInfo && !epmsSubscriptionInfo) {
newUrl.searchParams.set('epms_payment_uuid', epmsPaymentInfo.uuid)
newUrl.searchParams.set('epms_payment_status', epmsPaymentInfo.last_status)
window.location.href = newUrl.href
return
}
// EPMS subscription
if (epmsSubscriptionInfo && epmsPaymentSource) {
newUrl.searchParams.set('epms_subscription_uuid', epmsSubscriptionInfo.uuid)
newUrl.searchParams.set('epms_subscription_status', epmsSubscriptionInfo.status)
newUrl.searchParams.set('epms_payment_source_uuid', epmsSubscriptionInfo.payment_source_uuid)
newUrl.searchParams.set('epms_payment_source_status', epmsPaymentSource.last_status)
window.location.href = newUrl.href
return
}
// Important!
// it's very important to call "resolve()" function here.
// If you miss it, Tamaro form will not be shown and you will see
// endless loading spinner instead.
resolve()
})
})
window.rnw.tamaro.runWidget('#root', {
debug: false,
language: 'de',
})
</script>