> For the complete documentation index, see [llms.txt](https://lyzi.gitbook.io/developer/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://lyzi.gitbook.io/developer/getting-started/quickstart.md).

# Integration with API

## Resources

* [Doc générale](https://www.notion.so/API-Universal-V-2-1273d292350244e2bc189634b7b0a38a?pvs=21)
* [API](/developer/api/authentication.md)

## Introduction

To accept a cryptocurrency payment using Lyzi, two methods are available:

&#x20; A. Link the payment to a “Payment Button”

&#x20; B. Link the payment to a collector / salespoint.

The philosophy of those two methods is to use:

&#x20; A. Payment Button for online payments, in e-commerce websites, whereas

&#x20; B. ”Payment link” is more meant to be used for a in-store payment.

That being said, there is no strict limitation about using one or the other.

|                | A. Payment Button                               | B. Collector payment          |
| -------------- | ----------------------------------------------- | ----------------------------- |
| Ideal for      | eShop payments                                  | Payment link for a salespoint |
| Requires:      | A payment button                                |                               |
| (from the API) | One salespoint and one collector are configured |                               |

## Preliminary

To integrate Lyzi payment - in store or online - a “manager” account shall be **created** and **approved** by our compliance team (KYB process). Once this process is completed, the manager has full access to his backoffice, under <https://admin.lyzi.io/>. In there, the manager can create buy buttons, payment links, configure his salespoint, payment collectors and option cashier accounts.

{% hint style="info" %}
For testing purposes, a sandbox environment is also available and can be explored under <https://admin-dev.lyzi.io/>. Here are shared credentials to test the integration:

User : <manager-x@yopmail.com>

Pass: Lyzi123,
{% endhint %}

### Security

The following authorizations must be added in the header of the requests to Lyzi API:

```jsx
 'x-api-key': apiKey,
 'x-api-secret': apiSecret,
```

The key and the secret can be found in the backoffice, in the “developer” section.

### Return/callback URL

When integrating Lyzi payments, you need to handle two separate flows: front-end redirection for user experience, and back-end webhook for reliable transaction verification. Correctly distinguishing these ensures smooth UX and secure payment validation.

#### Front-end (redirection)

* `returnUrl` and `cancelUrl` are client-side redirection URLs.
* Lyzi opens the appropriate URL in the user’s browser after the payment completes (success → `returnUrl`, failure → `cancelUrl`).
* Use these only for UX changes and client-side confirmation.
* Do not rely on them for final payment validation.

#### Back-end (webhook)

* `webhookUrl` is the webhook endpoint. Lyzi sends a server-to-server POST to this URL with the transaction result.
* Your backend must accept and validate POST requests from Lyzi.

#### Combined behaviour

* If only `webhookUrl` is provided: Lyzi uses it as the webhook and will open it in the browser as a redirect (acts as return/cancel fallback).
* If `returnUrl`/`cancelUrl` are provided: browser redirection uses those URLs. If `webhookUrl` is also provided Lyzi still sends the webhook POST to `webhookUrl`.

{% hint style="info" %}
Always treat the webhook (callbackUrl) as the authoritative notification and validate the transaction via the Check Status endpoint on your server. Do not mark orders as paid based solely on front-end redirection or query parameters. Always confirm final status with the Check Status API.
{% endhint %}

## A. Buy button

As mentioned in the introduction, this approach is recommended for integration in online stores or applications.

### 1. Create the buy button

Before integrating the Lyzi payment solution on your website or application, you shall create a “Payment Button” from your [backoffice](https://admin.lyzi.io/).

First, make sure that your account has been created and that the KYB is done and confirmed. If you don’t have an account, go to <https://admin.lyzi.io/onboarding> and complete all the information of your profile.

Then, let’s create a payment button. Go to the backoffice and find the buy button section:

<figure><img src="/files/m9GOpfawQmIuGGj54u92" alt=""><figcaption></figcaption></figure>

Fill the information of the button, and create it.

<figure><img src="/files/H2XzqSLSBhFgnxI7F4yO" alt=""><figcaption></figcaption></figure>

As annoted in the picture, here are the information about the button:

1. **Name** : The name of the button for you to organize the created buttons. This is not published to the consumer.
2. **Website URL** : the website URL shown in the payment gate, to indicate to the consumer what website he is performing the payment for.
3. **Webhook URL** : The website for the redirection of the user after the payment (successfull or failed)
4. **Name of the product** : The name of the product that is being purchased. This is displayed in the payment gate to the consumer.

{% hint style="info" %}
Note that most of this information can be and **will be edited** during the payment initialization, when doing the integration in your application.
{% endhint %}

Once created, you can select the button that has been created and click on “Display” :

{% columns %}
{% column width="50%" %}

<figure><img src="/files/1Mx4FhTbcchCvcBl7ESv" alt=""><figcaption></figcaption></figure>
{% endcolumn %}

{% column width="50%" %}

<figure><img src="/files/1i1NWmTaTjCFmhKNRBXF" alt=""><figcaption></figcaption></figure>
{% endcolumn %}
{% endcolumns %}

### 2. Initiate the payment

In your application, we are going to use the buy button created in the previous section, customize its parameters and initiate the payment.

The simplest way to initiate a payment, based on this buy button, is to build the following URL, with the appropriate parameters :

```jsx
// This is the base URL for the payment using the button
const url = new URL("<https://pay.lyzi.io/buy-button/landing>");

// Set the query parameters
// - The ID of the button that has been created in the previous section
url.searchParams.append('id', buttonId;
// - The price of the payment to perform, as a string
url.searchParams.append('price', amount.toString());
// - The currency of the payment to perform, as a string. Only EUR, CHF and XPF are currently supported (as of 2025 Jan 1st)
url.searchParams.append('currency', "EUR");
// - The callback URL where the payment confirmation request will be POST'ed
url.searchParams.append('callbackUrl', callbackUrl);
// - The return URL, where the use will be redirected when the payment is completed and successfull
url.searchParams.append('returnUrl', returnUrl)
// - The return URL, where the use will be redirected when the payment is failed
url.searchParams.append('cancelUrl', cancelUrl)

// User can now be redirected to this URL, or a QR code can be built and shown to the user
router.push(url.toString())
// This will take the customer to the Lyzi Payment gate for him to perform the payment in cryptocurrencies
```

And voilà 💫 Just by a couple of lines, the payment url can be built and the user can be redirected to the payment gate to complete the payment.

Alternatively, the SDK can also be used to have more coding tool to intiate the payment:

## B. Direct payment

### Preliminary

The direct payment is used to link a payment to a collector, which is related to a salespoint. Before initiating the payment, the ID of the collector must be found.

Make sure that a collector has been created. Using the API or the backoffice :

#### Collector creation using the backoffice

{% columns %}
{% column width="25%" %}

<figure><img src="/files/5E46Y82rS9xEDiXzpAZb" alt=""><figcaption></figcaption></figure>
{% endcolumn %}

{% column width="75%" %}

<figure><img src="/files/QVKtWzLNhrAD6s89LXAK" alt=""><figcaption></figcaption></figure>
{% endcolumn %}
{% endcolumns %}

#### Collector creation using the API:

<details>

<summary><strong>API calls</strong></summary>

To create a collector, a salespoint must exist. Indeed, a collector is linked to a salespoint (you can see it as a cash register in a shop)

**Salespoint creation:**

```jsx
const salespoint = await axios.post(`${BASE_URL}/sales-points`,
        {
          name: person.companyName,
          city: person.city,
          country: "France",
          zipCode: person.zipCode,
          city: person.city,
          street: person.street,
          ...(logoUrl && {logoUrl}),
          ...(lat && {lat}),
          ...(lng && {lng}),
        } , {
          headers: {
            'Content-Type': 'application/json',
            'x-api-key': apiKey,
            'x-api-secret': apiSecret,
          },
        },
      );
```

**Collector creation, linked to the salespoint :**

```jsx
function getCollectorInfo(manager, companyId, salespointId) {
  const UUID = companyId;
  const POS_UUID = salespointId;
  const BRANDNAME = manager.name.replace(/\\s/g, '').toUpperCase();
  const COMPANYNAME = manager.companyName.replace(/\\s/g, '').toUpperCase();
  const GEN = Math.floor(10000000 + Math.random() * 90000000);
  return {
    identifier: `${UUID}_${POS_UUID}-${BRANDNAME}-${COMPANYNAME}-${GEN}`,
    uuid: UUID,
    salePoint: salespointId
  };
}

const collector = await axios.post(`${BASE_URL}/collectors/add`, getCollectorInfo(manager, companyId, salespointId),
      {
        headers: {
          'Content-Type': 'application/json',
          'x-api-key': apiKey,
          'x-api-secret': apiSecret,
        },
      });      
```

The response of this call contains the collector ID that is required to request a payment

</details>

### Collector ID

Once the collector is created, retrieve its ID :

```jsx
https://api-dev.lyzi.fr/api/next/collectors/list
```

The `identifier`in the response of this request shall be used in the field `merchantIdentifier` for of the request of the payment below.

### Request command:

To initialize a payment using cryptocurrencies, the endpoint `request` shall be called:

```jsx
https://api-dev.lyzi.fr/api/confirm_conversion/request
```

* Full reference : [Conversions](/developer/api/conversions.md#post-confirm_conversion-request)

#### Request body :

```jsx
{
        amount: Joi.required(),
        fromAsset: Joi.string().valid('EUR', 'CHF', 'XPF', 'USD', 'GBP', 'HKD').insensitive().required(),
        toAsset: Joi.string().valid('USDC', 'EUR').insensitive().default('USDC'),
        webhookUrl: Joi.string().allow(null, ''),
        cancelUrl: Joi.string().allow(null, '').max(256),
        returnUrl: Joi.string().allow(null, '').max(256),
        merchantIdentifier: Joi.string().allow(null, ''),
        anonymous: true,
        goods: Joi.object({
          goodsName: Joi.string(),
          goodsType: Joi.string().valid('01', '02'),
          goodsCategory: Joi.string().valid('0000', '1000', '2000', '3000', '4000', '5000', '6000', '7000', '8000', '9000', 'A000', 'B000', 'C000', 'D000', 'E000', 'F000', 'Z000'),
          goodsDetail: Joi.string().max(256).allow('', null),
          goodsQuantity: Joi.string().max(256).allow('', null)
        }),
        // If KYC has already been completed and agreement has been signed to 
        // share KYC information, the following data is required to bypass the KYC
        // procedure on Lyzi side.
        kycInfo: Joi.object({
          hash: Joi.string().required(),  // The HMAC hash, for security
          origin: Joi.string().required(),  // The origin name (fundora)
          id: Joi.string().required(),  // The session/KYC ID
          status: Joi.string().required(), // "approved" ? :) 
          firstName: Joi.string().required(),
          lastName: Joi.string().required(),
          nidPdfUrl: Joi.string().required(),  // The url where to download the file
          poaPdfUrl: Joi.string().required(),  // The url where to download the file
          kycReportUrl: Joi.string().required(),  // The url where to download the file
          fundOriginPdfUrl: Joi.string().required(),  // The url where to download the file
          approvedAt: Joi.string().required(), // Approval date
          rawData: Joi.object() // any data that can be useful
        }),
        // This data about the payer can be shared to avoid requesting him this information
        // during the payment flow (as part of the Travel Rule TFR). 
        // This may only be used for light KYC
        payerInformation: Joi.object({
          firstName: Joi.string().required(),
          lastName: Joi.string().required(),
          email: Joi.string().email().allow('', null),
          address1: Joi.string().allow('', null),
          address2: Joi.string().allow('', null),
          city: Joi.string().allow('', null),
          state: Joi.string().allow('', null),
          country: Joi.string().allow('', null),
          zipCode: Joi.string().allow('', null),
          postalAddress: Joi.string().required(),
          userAddress: Joi.string().allow('', null),
          isCompany: Joi.boolean().default(false),
          companyName: Joi.string().when('isCompany', { is: true, then: Joi.required(), otherwise: Joi.optional().allow('', null) }),
          companySiret: Joi.string().when('isCompany', { is: true, then: Joi.required(), otherwise: Joi.optional().allow('', null) }),
        }).optional(),
      }
```

And voilà 💫 Just by a couple of lines, the payment url (`paymentUrl` from the response) can be retrieved and the user can be redirected to the payment gate to complete the payment.

<details>

<summary>Example Response</summary>

```json
{
    "success": true,
    "data": {
        "_id": "69b136fc5123456f16c91b11",
        "code": "25356565699000113123456144012612345602000168",
        "shortCode": "ABCDEF",
        "fromAsset": "EUR",
        "fromAmount": "1.68",
        "toAsset": "USDC",
        "toAmount": "2.01642996",
        "exchangeInfo": {
            "symbol": "EURUSDC",
            "price": "1.1629710745848518",
            "_id": "69b136fc525a890f16c91b12"
        },
        "goods": {
            "goodsType": "01",
            "goodsCategory": "D000",
            "referenceGoodsId": "25356565699000113885417144012603113902000168",
            "goodsName": "Debit",
            "_id": "69b136fc525a890f16c91b13"
        },
        "status": "INITIAL",
        "expireTime": 1773222528723,
        "fees": {
            "total": "0.05040000",
            "totalAsset": "EUR",
            "totalConverted": "0.05873097",
            "totalConvertedAsset": "USDC",
            "percentage": "3.00",
        },
        "merchantIdentifier": "MY_IDENTIFIER",
        "paymentChannel": null,
        "isPayerAnonymous": true,
        "paymentUrl": "https://pay.lyzi.io/?code=25356565699000113123456144012612345602000168",
        "paymentChannelsConfig": [
            {
                "channelName": "binance",
                "persoInfoRequired": false,
                "kycRequired": false,
                "eddKycRequired": false,
                "_id": "69b136fc525a890f16c91b37"
            },
            {
                "channelName": "coinbase",
                "persoInfoRequired": true,
                "kycRequired": false,
                "eddKycRequired": false,
                "_id": "69b136fc525a890f16c91b38"
            },
            {
                "channelName": "kucoinpay",
                "persoInfoRequired": true,
                "kycRequired": false,
                "eddKycRequired": false,
                "_id": "69b136fc525a890f16c91b39"
            },
            {
                "channelName": "crypto",
                "persoInfoRequired": true,
                "kycRequired": false,
                "eddKycRequired": false,
                "_id": "69b136fc525a890f16c91b3a"
            },
            {
                "channelName": "whitepay",
                "persoInfoRequired": true,
                "kycRequired": false,
                "eddKycRequired": false,
                "_id": "69b136fc525a890f16c91b3b"
            }
        ],
        "createdAt": "2036-03-11T09:33:48.650Z",
        "updatedAt": "2036-03-11T09:33:48.761Z",
        "merchant": {
            "_id": "67ff77acbcdef09b4083aeb",
            "identifier": "MY_IDENTIFIER",
            "uuid": "f1a606e7-1234-1234-9876-f865ba092aa5",
            "active": true,
            "salePoint": "67ff77ac31234564e610f0859",
            "manager": {
                "company": { 
                    "name": "MY_COMPANY",
                    "allowedCurrencies": [
                        "EUR"
                    ],
                    "allowedCryptos": [
                        "1-ETH",
                        "1-USDC",
                        "137-POL",
                        "137-USDC",
                    ],
                    "nonCustodialBeta": true,
                    "skipKyc": false,
                    "hideStaticQr": true,
                    "allowedPaymentChannels": [
                        "binance",
                        "coinbase",
                        "kucoinpay",
                        "crypto",
                        "whitepay"
                    ],
                    "city": "Cannes",
                    "country": "FR",
                    "logoUrl": ""
                }
            },
            "cashiers": [],
            "createdAt": "2035-04-16T09:26:04.892Z",
            "updatedAt": "2035-04-16T09:26:04.892Z",
            "__v": 0
        },
        "refund": null,
        "payer": null,
        "anonymousPayer": null,
        "payerInfo": null,
        "cashback": null,
        "confirm": null,
        "orderRef": null,
        "allowedMaxRefundAmount": "1.68",
        "isFullyRefunded": false
    },
    "status": 200
}
```

</details>

{% hint style="info" %}
The `code` in the transaction details is a good element to save for order/payment tracking, especially to get the status of the payment session.&#x20;
{% endhint %}

## Check status

### Check status endpoint

To retrieve the current status of a payment session, use the following endpoint:

```bash
GET https://api-dev.lyzi.fr/api/confirm_conversion/status/:code
```

The `code` parameter is the transaction code returned in the `data.code` field of the `request` response.

* Full reference: [Conversions](/developer/api/conversions.md#get-confirm_conversion-status-code)

{% hint style="info" %}
Always use this endpoint to confirm payment status server-side. Do not rely solely on front-end redirection or webhook delivery to mark an order as paid.
{% endhint %}

#### **Request**

```http
GET https://api-dev.lyzi.fr/api/confirm_conversion/status/25356565699000113123456144012612345602000168
x-api-key: <your_api_key>
x-api-secret: <your_api_secret>
```

#### **Example response**

```json
{
  "success": true,
  "data": {
    "code": "25356565699000113123456144012612345602000168",
    "shortCode": "ABCDEF",
    "status": "PAID",
    "fromAsset": "EUR",
    "fromAmount": "1.68",
    "toAsset": "USDC",
    "toAmount": "2.01642996",
    "merchantIdentifier": "MY_IDENTIFIER",
    "createdAt": "2036-03-11T09:33:48.650Z",
    "updatedAt": "2036-03-11T09:35:12.000Z"
  },
  "status": 200
}
```

### **Status values**

The `status` field in the response can take the following values:

| Status                       | Description                                                               |
| ---------------------------- | ------------------------------------------------------------------------- |
| `INITIAL`                    | Order created, not yet submitted to a payment channel                     |
| `PENDING`                    | Payment accepted, processing in progress                                  |
| `AWAITING`                   | Sent to the exchange/CEX, payment not yet received                        |
| `PAID`                       | Payment completed successfully                                            |
| `CANCELLED`                  | Order was cancelled                                                       |
| `EXPIRED`                    | Order expired before payment was completed                                |
| `ERROR`                      | An error occurred during the payment process                              |
| `WAITING_CHAIN_CONFIRMATION` | On-chain transaction detected, waiting for block confirmation             |
| `KYT_FAILED`                 | Transaction blocked - failed Know Your Transaction (KYT) compliance check |
| `REFUNDING`                  | Refund initiated, in progress                                             |
| `REFUNDED`                   | Partial refund completed                                                  |
| `FULL_REFUNDED`              | Full refund completed                                                     |

{% hint style="info" %}
For a successful integration, the recommended polling or webhook verification flow is: `INITIAL` → `AWAITING` → `PENDING` → `PAID`. Only mark an order as confirmed once `PAID` status is received.
{% endhint %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://lyzi.gitbook.io/developer/getting-started/quickstart.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
