> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://amer.developers.trustly.com/llms.txt.
> For full documentation content, see https://amer.developers.trustly.com/llms-full.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://amer.developers.trustly.com/_mcp/server.

# Generate request signatures

A request signature is essential for the [Establish Data](/integrate/core-concepts/the-establish-data-object) used by the Trustly UI and the [POST /establish](/api-reference/api/transactions/post-establish) API endpoint to ensure the integrity of transactions, providing robust protection against potential tampering. The `requestSignature` should be generated server-side to keep your `accessKey` secure and out of client-side code. It is a Base64-encoded HMAC-SHA1 hash of the data in the request payload, created using your `accessKey`.

To generate a request signature, you complete the following tasks:

* **Concatenate Parameters** - Gather all the request parameters, including the `accessId` and then concatenate these parameters into a single string. Ensure the parameters are in the correct order.
* **Hash the Concatenated String** - Use the HMAC-SHA1 hashing algorithm, use your `accessKey` as the key for the HMAC algorithm, and then Base64 encode the resulting hash.
* **Attach the Signature to Your Request** - Include the generated signature in the `requestSignature` property of the payload.

## Hashing algorithm options (BETA functionality)

If desired, an alternative hashing algorithm may be used to generate the requestSignature. Trustly will assume by default that HMAC-SHA1 has been used to generate the hash so if an alternative is used, the algorithm name and a colon should be prepended to the resulting signature after it is Base64 encoded. For example:

```
requestSignature: "HmacSHA512:RuYv5esOLn2f4F4NU5bz7YGLITEtLVQrciiEm0dCrn/O1DJ9E5hLwIYTyd5DHBJBxAhdxuKp655bG/gymoPt+g=="
```

Supported algorithms include:

* HMAC-SHA1: `HmacSHA1` (Default value if no algorithm prefix is included)
* HMAC-SHA512: `HmacSHA512`

## Parameter ordering

In order for the signature to be validated correctly, the order of parameters in the hash must be strictly maintained. Any field in the list below which is present in the request payload must be included in the generated `requestSignature` and must appear in the order below.

Do **not** include any parameters that are not present in the request payload. Additionally, do not include fields with `null` values unless they also appear in your request payload with a `null` value.

* accessId
* merchantId
* description
* currency
* amount
* displayAmount
* minimumBalance
* merchantReference
* paymentType
* timeZone
* recurrence.startDate
* recurrence.endDate
* recurrence.frequency
* recurrence.frequencyUnit
* recurrence.frequencyUnitType
* recurrence.recurringAmount
* recurrence.automaticCapture
* verification.status
* verification.verifyCustomer
* customer.customerId
* customer.externalId
* customer.name
* customer.vip
* customer.taxId
* customer.driverLicense.number
* customer.driverLicense.state
* customer.address.address1
* customer.address.address2
* customer.address.city
* customer.address.state
* customer.address.zip
* customer.address.country
* customer.phone
* customer.email
* customer.balance
* customer.currency
* customer.enrollDate
* customer.externalTier
* customer.externalTierTrustScore
* customer.dateOfBirth
* account.nameOnAccount
* account.name
* account.type
* account.profile
* account.accountNumber
* account.routingNumber
* beneficiary.name
* beneficiary.taxId
* beneficiary.address.address1
* beneficiary.address.city
* beneficiary.address.state
* beneficiary.address.zip
* beneficiary.address.country
* beneficiary.dateOfBirth
* beneficiaryAccount.iban
* beneficiaryAccount.paymentProvider.name
* beneficiaryAccount.paymentProvider.routingNumber
* beneficiaryAccount.paymentProvider.swift
* beneficiaryAccount.paymentProvider.country
* transactionId
* onlinePPSubtype
* customer.customData.payins.volume30Days
* customer.customData.payins.volume90Days
* customer.customData.payins.volume365Days
* customer.customData.payouts.volume30Days
* customer.customData.payouts.volume90Days
* customer.customData.payouts.volume365Days

## Examples

For a functional example in the context of a backend application, see the [NestJS example](https://github.com/TrustlyInc/trustly-nestjs-example/blob/main/src/RequestSignature/request-signature.utils.ts). The following examples are provided here:

* Readable but verbose JavaScript example
* Optimized ES6 JavaScript example
* Java example

```javascript
const Crypto = require('crypto');
const generateSignature = (establishData, accessKey) => {
    let query = '';
    query += `accessId=${establishData.accessId}`;
    query += `&merchantId=${establishData.merchantId}`;
    query += `&description=${establishData.description}`;
    query += `&currency=${establishData.currency}`;
    query += `&amount=${establishData.amount}`;

    if (establishData.displayAmount) query += `&displayAmount=${establishData.displayAmount}`;
    if (establishData.minimumBalance) query += `&minimumBalance=${establishData.minimumBalance}`;

    query += `&merchantReference=${establishData.merchantReference}`;
    query += `&paymentType=${establishData.paymentType}`;

    if (establishData.timeZone) query += `&timeZone=${establishData.timeZone}`;

    if (establishData.paymentType === 'Recurring' && establishData.recurrence) {
        if (establishData.recurrence.startDate) query += `&recurrence.startDate=${establishData.recurrence.startDate}`;
        if (establishData.recurrence.endDate) query += `&recurrence.endDate=${establishData.recurrence.endDate}`;
        if (establishData.recurrence.frequency) query += `&recurrence.frequency=${establishData.recurrence.frequency}`;
        if (establishData.recurrence.frequencyUnit) query += `&recurrence.frequencyUnit=${establishData.recurrence.frequencyUnit}`;
        if (establishData.recurrence.frequencyUnitType) query += `&recurrence.frequencyUnitType=${establishData.recurrence.frequencyUnitType}`;
        if (establishData.recurrence.recurringAmount) query += `&recurrence.recurringAmount=${establishData.recurrence.recurringAmount}`;
        if (establishData.recurrence.automaticCapture) query += `&recurrence.automaticCapture=${establishData.recurrence.automaticCapture}`;
    }

    if (establishData.verification) {
        if (establishData.verification.status) query += `&verification.status=${establishData.verification.status}`;
        if (establishData.verification.verifyCustomer) query += `&verification.verifyCustomer=${establishData.verification.verifyCustomer}`;
    }

    if (establishData.customer) {
        if (establishData.customer.customerId) query += `&customer.customerId=${establishData.customer.customerId}`;
        if (establishData.customer.externalId) query += `&customer.externalId=${establishData.customer.externalId}`;
        if (establishData.customer.name) query += `&customer.name=${establishData.customer.name}`;
        if (establishData.customer.vip !== undefined) query += `&customer.vip=${establishData.customer.vip}`;
        if (establishData.customer.taxId) query += `&customer.taxId=${establishData.customer.taxId}`;
        if (establishData.customer.driverLicense) {
            if (establishData.customer.driverLicense.number) query += `&customer.driverLicense.number=${establishData.customer.driverLicense.number}`;
            if (establishData.customer.driverLicense.state) query += `&customer.driverLicense.state=${establishData.customer.driverLicense.state}`;
        }
        if (establishData.customer.address) {
            if (establishData.customer.address.address1) query += `&customer.address.address1=${establishData.customer.address.address1}`;
            if (establishData.customer.address.address2) query += `&customer.address.address2=${establishData.customer.address.address2}`;
            if (establishData.customer.address.city) query += `&customer.address.city=${establishData.customer.address.city}`;
            if (establishData.customer.address.state) query += `&customer.address.state=${establishData.customer.address.state}`;
            if (establishData.customer.address.zip) query += `&customer.address.zip=${establishData.customer.address.zip}`;
            if (establishData.customer.address.country) query += `&customer.address.country=${establishData.customer.address.country}`;
        }
        if (establishData.customer.phone) query += `&customer.phone=${establishData.customer.phone}`;
        if (establishData.customer.email) query += `&customer.email=${establishData.customer.email}`;
        if (establishData.customer.balance) query += `&customer.balance=${establishData.customer.balance}`;
        if (establishData.customer.currency) query += `&customer.currency=${establishData.customer.currency}`;
        if (establishData.customer.enrollDate) query += `&customer.enrollDate=${establishData.customer.enrollDate}`;
        if (establishData.customer.dateOfBirth) query += `&customer.dateOfBirth=${establishData.customer.dateOfBirth}`;
    }

    if (establishData.account) {
        if (establishData.account.nameOnAccount) query += `&account.nameOnAccount=${establishData.account.nameOnAccount}`;
        if (establishData.account.name) query += `&account.name=${establishData.account.name}`;
        if (establishData.account.type) query += `&account.type=${establishData.account.type}`;
        if (establishData.account.profile) query += `&account.profile=${establishData.account.profile}`;
        if (establishData.account.accountNumber) query += `&account.accountNumber=${establishData.account.accountNumber}`;
        if (establishData.account.routingNumber) query += `&account.routingNumber=${establishData.account.routingNumber}`;
    }
  
  // Required for International Money Transfer
  if (establishData.beneficiary) {
      if (establishData.beneficiary.name) query += `&beneficiary.name=${establishData.beneficiary.name}`;
      if (establishData.beneficiary.taxId) query += `&beneficiary.taxId=${establishData.beneficiary.taxId}`;
      if (establishData.beneficiary.address) {
          if (establishData.beneficiary.address.address1) query += `&beneficiary.address.address1=${establishData.beneficiary.address.address1}`;
          if (establishData.beneficiary.address.city) query += `&beneficiary.address.city=${establishData.beneficiary.address.city}`;
          if (establishData.beneficiary.address.state) query += `&beneficiary.address.state=${establishData.beneficiary.address.state}`;
          if (establishData.beneficiary.address.zip) query += `&beneficiary.address.zip=${establishData.beneficiary.address.zip}`;
          if (establishData.beneficiary.address.country) query += `&beneficiary.address.country=${establishData.beneficiary.address.country}`;
      }
      if (establishData.beneficiary.dateOfBirth) query += `&beneficiary.dateOfBirth=${establishData.beneficiary.dateOfBirth}`;
     }
  if (establishData.beneficiaryAccount) {
      if (establishData.beneficiaryAccount.paymentProvider) {
          if (establishData.beneficiaryAccount.paymentProvider.name) query += `&beneficiaryAccount.paymentProvider.name=${establishData.beneficiaryAccount.paymentProvider.name}`;
          if (establishData.beneficiaryAccount.paymentProvider.routingNumber) query += `&beneficiaryAccount.paymentProvider.routingNumber=${establishData.beneficiaryAccount.paymentProvider.routingNumber}`;
          if (establishData.beneficiaryAccount.paymentProvider.swift) query += `&beneficiaryAccount.paymentProvider.swift=${establishData.beneficiaryAccount.paymentProvider.swift}`;
          if (establishData.beneficiaryAccount.paymentProvider.country) query += `&beneficiaryAccount.paymentProvider.country=${establishData.beneficiaryAccount.paymentProvider.country}`;
      }
      if (establishData.beneficiaryAccount.iban) query += `&beneficiaryAccount.iban=${establishData.beneficiaryAccount.iban}`;
  }

    if (establishData.transactionId) query += `&transactionId=${establishData.transactionId}`;

    const requestSignature = Crypto.createHmac('sha1', accessKey).update(query).digest('base64');
    return requestSignature;
}
```

```Text Optimized Javascript
const crypto = require('crypto');

// Ordered and final keys list
const signatureKeysOrdered = ['accessId', 'merchantId', 'description', 'currency', 'amount', 'displayAmount', 'minimumBalance', 'merchantReference', 'paymentType', 'timeZone', 'recurrence.startDate', 'recurrence.endDate', 'recurrence.frequency', 'recurrence.frequencyUnit', 'recurrence.frequencyUnitType', 'recurrence.recurringAmount', 'recurrence.automaticCapture', 'verification.status', 'verification.verifyCustomer', 'customer.customerId', 'customer.externalId', 'customer.name', 'customer.vip', 'customer.taxId', 'customer.driverLicense.number', 'customer.driverLicense.state', 'customer.address.address1', 'customer.address.address2', 'customer.address.city', 'customer.address.state', 'customer.address.zip', 'customer.address.country', 'customer.phone', 'customer.email', 'customer.balance', 'customer.currency', 'customer.enrollDate', 'customer.externalTier', 'customer.dateOfBirth', 'account.nameOnAccount', 'account.name', 'account.type', 'account.profile', 'account.accountNumber', 'account.routingNumber', 'transactionId'];

// Removed 'export' to ensure compatibility with standard Node.js scripts
const generateSignature = (establishData, accessKey) => {
  const searchParams = [];

  // Use only parameters present on the key list
  for (const key of signatureKeysOrdered) {
    // If the key doesn't have a child, push it now
    if (!key.includes('.')) {
      establishData[key] && searchParams.push(`$&#123;key&#125;=${establishData[key].toString()}`);
      continue;
    }

    // If the key has a child, split it into sub-keys
    const subKeys = key.split('.');
    let data = establishData;

    // For each sub-key, set its value
    for (const subKey of subKeys) {
      const innerValue = data[subKey];

      if (typeof innerValue === 'object') {
        // If the sub-key has a sub-key, process it again
        data = innerValue;
      } else {
        // If not, push it
        innerValue && searchParams.push(`$&#123;key&#125;=${innerValue.toString()}`);
        break;
      }
    }
  }

  // Concatenate all parameters into a plain string
  const query = searchParams.join('&');

  // Encode the string
  const requestSignature = crypto.createHmac('sha1', accessKey).update(query).digest('base64');

  return requestSignature;
};
```

```java
public class TrustlySignature {

  public static class Account { //Used only on account verification
    public String nameOnAccount;
    public String name;
    public String type;
    public String profile;
    public String accountNumber;
    public String routingNumber;
  }

  public static class Recurrence { //Used only when payment type is Recurring
    public Date startDate;
    public Date endDate;
    public Integer frequency;
    public Integer frequencyUnit;
    public Integer frequencyUnitType;
    public BigDecimal recurringAmount;
    public Boolean automaticCapture;
  }

  public static class Address {
    public String address1;
    public String address2;
    public String city;
    public String state;
    public String zip;
    public String country;
  }

  public static class DriverLicense {
    public String number;
    public String state;
  }

  public static class Customer {
    public String customerId; //Trustly customer ID
    public String externalId; //Merchant’s customer ID
    public String name;
    public String vip;
    public String taxId;
    public DriverLicense dL;
    public Address address;
    public String phone;
    public String email;
    public String balance;
    public String currency;
    public String enrollDate;
    public String dateOfBirth;
  }

  public static class Verification {
    public String status;
    public Boolean verifyCustomer;
  }

  public String createRequestSignature(String accessId,
                                       String accessKey,
                                       String merchantId,
                                       String description,
                                       String currency,
                                       BigDecimal amount,
                                       BigDecimal displayAmount,
                                       BigDecimal minimumBalance,
                                       String merchantReference,
                                       String paymentType,
                                       String timeZone,
                                       Recurrence r,
                                       Verification v,
                                       Customer c,
                                       Account a,
                                       String transactionId) throws Exception {
    String str = "accessId="+ accessId;
		str += "&merchantId="+merchantId;
    str += "&description="+description;
    str += "&currency="+currency;
    str += "&amount="+amount.setScale(2); //2 decimal places such as 13.25
    if(displayAmount != null) str += "&displayAmount="+displayAmount.setScale(2); //2 decimal places such as 13.25
    if(minimumBalance != null) str += "&minimumBalance="+minimumBalance.setScale(2); //2 decimal places such as 13.25
    str += "&merchantReference="+merchantReference;
    str += "&paymentType="+paymentType;
    if(timeZone != null) str += "&timeZone="+timeZone;

    if("Recurring".equals(paymentType) && r != null){
      if(r.startDate != null) str += "&recurrence.startDate=" + r.startDate.getTime();
      if(r.endDate != null) str += "&recurrence.endDate=" + r.endDate.getTime();
      if(r.frequency != null) str += "&recurrence.frequency="+r.frequency;
      if(r.frequencyUnit != null) str += "&recurrence.frequencyUnit="+r.frequencyUnit;
      if(r.frequencyUnitType != null) str +="&recurrence.frequencyUnitType="+r.frequencyUnitType;
      if(r.recurringAmount != null) str +="&recurrence.recurringAmount="+r.recurringAmount;
      if(r.automaticCapture != null) str +="&recurrence.automaticCapture="+r.automaticCapture;
    }

    if (v != null) {
      if(v.status != null) str +="&verification.status=" + v.status;
      if(v.verifyCustomer != null) str +="&verification.verifyCustomer=" + v.verifyCustomer;      
    }

    if(c != null) {
      if(c.customerId != null) str +="&customer.customerId=" + c.customerId;
      if(c.externalId != null) str +="&customer.externalId=" + c.externalId;
      if(c.name != null) str +="&customer.name=" + c.name;
      if(c.vip != null) str +="&customer.vip=" + c.vip;
      if(c.taxId != null) str +="&customer.taxId=" + c.taxId;

      if(c.dL != null) {
	      if(c.dL.number != null) str +="&customer.driverLicense.number=" + c.dL.number;
        if(c.dL.state != null) str +="&customer.driverLicense.state=" + c.dL.state;
      }

      if(c.address != null) {
        if(c.address.address1 != null) str +="&customer.address.address1=" + c.address.address1;
        if(c.address.address2 != null) str +="&customer.address.address2=" + c.address.address2;
        if(c.address.city != null) str +="&customer.address.city=" + c.address.city;
        if(c.address.state != null) str +="&customer.address.state=" + c.address.state;
        if(c.address.zip != null) str +="&customer.address.zip=" + c.address.zip;
        if(c.address.country != null) str +="&customer.address.country=" + c.address.country;
      }
      if(c.phone != null) str +="&customer.phone=" + c.phone;
      if(c.email != null) str +="&customer.email=" + c.email;
      if(c.balance != null) str +="&customer.balance=" + c.balance.setScale(2);      
      if(c.currency != null) str +="&customer.currency=" + c.currency;
      if(c.enrollDate != null) str +="&customer.enrollDate=" + c.enrollDate;
      if(c.dateOfBirth != null) str +="&customer.dateOfBirth=" + c.dateOfBirth;
    }

    if(a != null) {
      if(a.nameOnAccount != null) str +="account.nameOnAccount=" + a.nameOnAccount;
      if(a.name != null) str +="account.name=" + a.name;
      if(a.type != null) str +="account.type=" + a.type;
      if(a.accountNumber!= null) str +="account.accountNumber=" + a.accountNumber;
      if(a.routingNumber!= null) str +="account.routingNumber=" + a.routingNumber;
    }

    if(transactionId != null) str += "&transactionId="+transactionId;

    return TrustlySignature.doHashing(str,accessKey);
  }

  public static String doHashing(String str, String accessKey) throws Exception {
    Mac = Mac.getInstance("HmacSHA1");
    SecretKeySpec signingKey = new SecretKeySpec(accessKey.getBytes(),"HmacSHA1");
    mac.init(signingKey);
    byte[] rawHmac = mac.doFinal(str.getBytes());
    return encodeBase64(rawHmac);
  }

  public static String encodeBase64(byte[] binary){
    return DatatypeConverter.printBase64Binary(binary);
  }
}
```