@raisenow/tamaro-core

2.8.3 • Public • Published

Introduction

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.

Concepts

Hence, Tamaro is quite complex and flexible, below is a brief description of the various concepts that are being used for Tamaro configuration.

Conditionals

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

Events

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

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.

Configuration

Tamaro can be configured using different ways:

  • during build process
  • in runtime

Configuration during build process

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 as window.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.

Configuration 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.

Configuration options

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

E-mail configuration

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.

Parameters

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

Templates

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

Example

{{greeting}},

You have donated **{{amount}}**
{{payment_method_preposition}} {{payment_method}}.

{{#if additional_information}}
Additionally, you left the following information.
{{#each additional_information}}

**{{this.description}}**
{{this.content}}
{{/each}}
{{/if}}

{{#if custom_parameters.some_custom_identifier}}
Some custom identifier: {{custom_parameters.some_custom_identifier}}
{{/if}}

{{#if supporter.email_permission}}
You asked to receive news from us on {{supporter.email}}.
You can unsubscribe at any time.
{{/if}}

Thank you for your donation,
{{organisation.name}}

Legacy e-mail service

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.

Examples

Debug and test mode

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

Debug options

To facilitate debugging of data you can set these options:

# config.yml

debug_error_messages: true
debug_slots: true

Specify language

Change widget language.

Default value is 'en'.

# config.yml

language: de

Specify fallback language

This language will be used for case, which don't have translations.

Default value is 'en'.

# config.yml

fallback_language: de

Flows

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.

Field mappings for EPMS flow

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.

Main supporter fields

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

Supporter metadata fields

EPP EPMS
stored_customer_pobox supporter.metadata.post_office_box
stored_customer_{fieldname} supporter.metadata.{fieldname}

Raisenow integration fields

EPP EPMS
stored_customer_donation_receipt raisenow.integration.donation_receipt_requested
stored_customer_message raisenow.integration.message

Supporter raisenow parameters fields

Additionaly to supporter.email_permission the following is also set:

EPP EPMS
stored_customer_email_permission supporter.raisenow_parameters.integration.opt_in.email

Raisenow product fields

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

Custom parameters fields

EPP EPMS
stored_{fieldname} custom_parameters.{fieldname}

Providing translations

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

Interpolated translations

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

Configuring blocks

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.

Show / hide standard form fields

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.

Required fields

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.

Current main rules

  • 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)

Only submit visible standard fields

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

Prefill payment form

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

EPP config

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 resolve apiKey, merchantId, successUrl, errorUrl, cancelUrl and immediateExecution.
    • testMode forcibly resolves as true.
  • If set to "prod":
    • epp config option is used to resolve apiKey, merchantId, successUrl, errorUrl, cancelUrl and immediateExecution.
    • testMode resolves as value specified in the root level of config (either true or false).

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.

EPMS config

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 resolve accountUuid, paymentMethodProfile, stripePublicKey and stripeAccount.
    • testMode forcibly resolves as true.
  • If set to "prod":
    • epms config option is used to resolve accountUuid, paymentMethodProfile, stripePublicKey and stripeAccount.
    • testMode resolves as value specified in the root level of config (either true or false).

It's possible to use conditions:

  • For EPMS properties in epms and epmsStage config options: accountUuid, stripePublicKey, stripeAccount.
  • For each specific paymentMethodProfile parameter in epms and epmsStage 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.

Specify available purposes

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

Specify available payment methods, types and intervals

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>

Specify allowed card types

You can specify which card types are supported.

Default value is undefined, meaning all card types are supported.

allowed_card_types:
  - vis
  - eca
  - amx

Specify priority card types

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

Set available currencies

You can set a limit of certain currencies to widget:

# config.yml

currencies:
  - chf
  - usd

Set amounts

Specify available amounts.

# config.yml

amounts:
  - 5
  - 35
  - 150

Autoselect purpose, payment type, amount, payment method

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

Purpose details

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

Adjust amount subunits

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

Displaying custom amount

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

Define minimum custom amount

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

Define maximum custom amount

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

Set payment provider

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

Wallet payments (Apple/Google Pay)

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.

Set allowed recurring payment methods

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

FAQ Entries

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

Set available salutations

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

Set available countries

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

Set priority countries

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

Validation of the payment form

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

Creating custom validation rules

// 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)

UI breakpoints

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
}

UI invalid elements scope

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"

UI scroll offsets

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

UI heading level

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

Show / hide submit button

Option to show/hide submit button on payment form.

Default value is true.

# config.yml

show_submit_button: true

Show / hide footer

Option to show/hide footer.

Default value is true.

# config.yml

show_footer: true

Show / hide test mode bar

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

Show / hide retry payment button

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

Recurring intervals layout

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"

Allow Swiss QR Bill Order

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

Require Swiss QR Bill reference number

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

Analytics Event Tracking

Enable/disable analytics event tracking.

Default value is the following:

# config.yml

analytics_event_tracking:
  - if: testMode() == true
    then: false
    else: true

AB Testing

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"
    }
  }
}

Cover fees

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

Direct debit form template

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

One-Click Payment

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

Debug PCI Proxy

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

Configuring recaptcha

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 amount descriptions

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.

Specifying supported card types

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

Configuring slots

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

Error logging

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

Update form data on retry payment

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

Donation receipt requires fields

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

Autogenerated donation receipt enabled

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

Organisation country code

Config option organisationCountryCode is used to apply different rules when donation receipt checkbox is checked.

Default value: undefined.

# config.yml

organisation_country_code: CH

Dynamic components

As already mentioned, you can place dynamic components into slots, below is a summary of available components.

block

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: []

block_header

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

block_content

Place it right after the block_header inside a block component.

component: block_content
children: []

content

Allows to set html content.

component: content
text_html: translation_key

field

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

text, textarea, date

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

radio-group and radio-in-group

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>

checkbox

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>

checkbox-group and checkbox-in-group

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>

select

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

select-salutations, select-countries, select-states

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

Dynamic fields complete example

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 if stored_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)

Redirect to custom Result page

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>

Readme

Keywords

none

Package Sidebar

Install

npm i @raisenow/tamaro-core

Weekly Downloads

144

Version

2.8.3

License

none

Unpacked Size

3.48 MB

Total Files

78

Last publish

Collaborators

  • tsuy
  • brunnerlivio
  • igor_shpyrko
  • sigerello_bytesoft
  • michschm
  • rmatil_rnw