Documentation
Backend

Backend

You need to follow some steps so your LiveApp can communicate properly with our Backend.

In addition to the following steps, Ledger also needs to know:

  • The base url of the API,
  • Any backend authentication information to use the api (token in header...)

Endpoints needed for Card

In order to communicate with Ledger’s backend, you must provide standardised APIs for Ledger's CARD aggregator services to call.

🔗

You will find all the information regarding the CARD endpoints here: https://exchange-integration-fund.redoc.ly/ (opens in a new tab), as well as additional details on some endpoints below.

You need to implement following endpoint for the card:

Additional Details: POST /sell

Example request payload:

  {
  "quoteId": "string",
  "amount": "string",
  "refundAddress": "string",
  "fromCryptoCurrency": "string",
  "toFiatCurrency": "string",
  "payloadCryptoCurrency": "string",
  "nonce": "number",
  }

An example response from the your backend to the Ledger backend

{
"sellId": "SELL-ID-165940",
"payinAddress": "0xa0b86991c627e936c1d19d4a2e90a2ce3606eb48",
"createdAt": "2030-05-26T14:13:39",
"providerFees": "0.0001",
"referralFees": "0.0001",
"payoutNetworkFees": "0.0002",
"providerSig": {
  "payload": "CgUweGZmZhoFMHhmZmYqBTB4ZmZmOgNCVENCA0JBVEoIMTIwMDAwMDBSCDExNTAwMDAwWhF2ZXJ5IGxvbmd1ZSBub25jZQ==",
  "payload_signature": "MEUCIBRm4PrdgRw0aBwRepuOGGRmR/YPcCoyKNJ7UDjFO030AiEA/VT0anolum0a3X/9lGPeovZHqzeDG9brcUB4zhYmwbs="
}
}

Your Protobuf message should have the following structure:

syntax = "proto3";
package ledger_sell;
 
// (coefficient) * 10^(- exponent)
message UDecimal {
    bytes  coefficient = 1;
    uint32 exponent = 2;
}
 
message NewSellResponse {
    string   trader_email = 1;           
    string   in_currency = 2;            
    bytes    in_amount = 3;              
    string   in_address = 4;             
    string   out_currency = 5;           
    UDecimal out_amount = 6;             
    bytes    device_transaction_id = 7;  
}

Real-time Status Updates:

To ensure effective communication with Ledger's backend, you are required to implement a webhook /transaction/{sellId}/status (opens in a new tab) update mechanism. This should notify Ledger real-time whenever there is a change in the status of a transaction. Ledger will provide partners with the necessary webhook URL or address to which the status updates should be sent.

Additional Details: POST /transaction/{sellId}/status

Explanation of the different output status:

  • FINISHED: Trade has been completed successfully (user has received payout transaction).
  • EXPIRED: Payin transaction was not received in time, trade is cancelled. User will be refunded if payin transaction is received afterwards.
  • ON_HOLD: Trade has been put on hold (eg: for KYC reasons). User must contact support.
  • PENDING: Trade is in progress (provider is waiting to receive payin transaction, or user is waiting to receive payout transaction)
  • TRANSFER_IN_COMPLETED: Transfer in completed and waiting for transfer out to be sent.
  • REFUNDED: Trade has been cancelled, refund transaction has been successfully received by user.
  • UNKNOWN: Trade is in unknown state. User must contact support.

Payload & Signature

Here is a little diagram to explain how the payload and the signature are generated:

Payload and Payload Signature generation diagram

Signature usage

  • Payload and Signature
    From the provider to Ledger Live. The payload is a protobuf message containing the trade data. It is generated by the provider and sent to Ledger Live.
type Payload struct {
  trader_email,
  in_currency,
  in_amount,
  in_address,
  out_currency,
  out_amount,
  device_transaction_id,
}
R, S := Sign(payload, privKey)
  • Validate
    Exchange app checks and validates the payload (signature and content).
// Compare nonce in payload
Verify((R, S), payload, pubKey)
  • Display
    Exchange app requests approval to the user by displaying the operation summary on screen.
Validate?
Send currency_from amount_to_provider
Receive currency_to amount_to_wallet

Input field: nonce

A nonce field will be passed as a parameter of the /sell endpoint. It is a 32 bytes nonce which is generated by the hardware wallet to avoid replay attacks.
It will be base 64 URL encoded before being sent to the /sell endpoint.

Output field: providerSig

The real return value of the /sell endpoint is the providerSig field with the JSON Web Signature (JWS) in compact form within:

  • providerSig.payload - base64 URL of the binary serialized protobuf message.NewTransactionResponse.
  • providerSig.signature - base64 URL of the ES256 signature of providerSig.payload. More details in the JWS signature section.

JWS-Signature

The output field is providerSig, and is based on the flattened JWS JSON (opens in a new tab), without the optional protected and header parameters.

The algorithms supported by Ledger are ES256 (opens in a new tab) with secp256k1 or secp256r1 curve. There is no need to specify them into the header, as we will store your public key with this information in our system.

As specified in the JWA spec, the signature is a concatenation of the (R, S) pair in a 64 octets array, and not the ASN1 format.

The JWS RFC (opens in a new tab) requires the signature to be computed on: <BASE64URL_HEADER>.<BASE64URL_PAYLOAD>. As we don't require any header, the signature has to be computed on: .<BASE64URL_PAYLOAD>.

Ledger
Copyright © Ledger SAS. All rights reserved. Ledger, Ledger Nano S, Ledger Vault, Bolos are registered trademarks of Ledger SAS