API - Sign requests

Introduction

It’s crucial to maintain the integrity of our Checkout API to ensure that its content remains untampered. To achieve this, we are introducing a signature requirement for the Checkout API. When making direct calls to the Checkout API, it's necessary to sign the following and send the signature if they are present in the request payload:

  • walletAddress
  • walletMemo

How you can generate the signature ?

  1. Prepare the String to be Signed
    First, prepare the string that needs to be signed in the specified format. For our API integrations, the keys that require signing are walletAddress and walletMemo.

    const signContent = 'walletAddress=1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa&walletMemo=1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2';
    
  2. Order Keys Alphabetically
    Ensure that keys in signContent are ordered alphabetically. Consistent formatting helps minimize signature validation errors.

    You can use the following function to convert your input URL string to a valid format.


    function arrangeStringAlphabetically(inputString: string): string {
        // Parse the input string into an object
        const inputObject: { [key: string]: { [key: string]: string } } = {};
        inputString.split('&').forEach((pair) => {
            // Split each pair into key and value
            const [key, value] = pair.split('=');
            // Split the value into nested key-value pairs
            const nestedPairs = value.split(',');
            inputObject[key] = {}; // Initialize the nested object for the key
            nestedPairs.forEach((nestedPair) => {
                // Split each nested pair into nested key and value
                const [nestedKey, nestedValue] = nestedPair.split(':');
                // Assign the nested key-value pair to the nested object
                inputObject[key][nestedKey] = nestedValue;
            });
        });
    
        // Sort the keys of each nested object alphabetically
        for (const key in inputObject) {
            inputObject[key] = Object.fromEntries(Object.entries(inputObject[key]).sort());
        }
    
        // Sort the keys of the top-level object alphabetically
        const sortedKeys = Object.keys(inputObject).sort();
        const sortedObject: { [key: string]: { [key: string]: string } } = {};
        sortedKeys.forEach((key) => {
            sortedObject[key] = inputObject[key];
        });
    
        // Reconstruct the string from the sorted object
        let resultString = '';
        for (const key in sortedObject) {
            resultString += key + '='; // Append the key
            // Append nested key-value pairs, sorted alphabetically
            resultString += Object.entries(sortedObject[key]).map(([nestedKey, nestedValue]) => `${nestedKey}:${nestedValue}`).join(',');
            resultString += '&'; // Separate key-value pairs with '&'
        }
        resultString = resultString.slice(0, -1); // Remove the trailing '&'
    
        return resultString;
    }
    
  3. Generate the Signature.
    After preparing signContent, use HMAC with SHA256 hashing to generate the signature in hexadecimal format.


    Note: We will provide a separate secret key specifically for signing the signContent. Please contact our customer support for assistance in obtaining the key.


    import crypto from "crypto";
    
    function generateSignature(secretKey: string, data: string): string {
        const hmac = crypto.createHmac("sha256", secretKey);
        hmac.update(data);
        return hmac.digest("hex");
    }
    

  4. Handling API Requests
    The generated signature and signContent should be included in the Checkout API request body along with the other checkout API data as shown below:


    {
       body: {
        signature:
          'a1b8609646d0f37d31970fa37e6ae37215aaada91be418b09575dbe8d510d04d',
        signContent:
          'walletAddress=1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa&walletMemo=1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2',
        onramp: 'coinify',
        source: 'eur',
        destination: 'btc',
        amount: 200,
        type: 'buy',
        paymentMethod: 'creditcard',
        network: 'bitcoin',
        uuid: '6756256e-d07f-42f0-a873-4d992eec09',
        originatingHost: 'buy.onramper.dev',
        partnerContext: '123-CLIENT-ORDER-ID-456',
        wallet: {
          address: '1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa',
          memo: '1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2',
        },
        allowOnrampPmSelection: false,
        supportedParams: {
          theme: {
            isDark: false,
            themeName: 'light-theme',
            primaryColor: '#241D1C',
            secondaryColor: '#FFFFFF',
            primaryTextColor: '#141519',
            secondaryTextColor: '#6B6F80',
            cardColor: '#F6F7F9',
          },
          partnerData: {
            redirectUrl: {
              success: 'http%3A%2F%2Fredirecturl.com%2F',
            },
          },
        },
      },
    }