# Vos clés d’API

Clés de test et de production, secrètes et publiables, et votre première requête authentifiée.

Chaque requête à l’API AdProMaxPay porte une clé. La clé indique à l’API quelle organisation vous êtes
et dans quel mode vous travaillez, test ou production.

## Les quatre sortes de clés

| Préfixe         | Sorte     | Mode       |
| --------------- | --------- | ---------- |
| `apx_sk_test_…` | Secrète   | Test       |
| `apx_pk_test_…` | Publiable | Test       |
| `apx_sk_live_…` | Secrète   | Production |
| `apx_pk_live_…` | Publiable | Production |

* Une **clé secrète** agit pour toute votre organisation. Sa place est sur votre serveur, jamais dans
  une page web ou une application.
* Une **clé publiable** est faite pour figurer dans une page web ou une application. Aujourd’hui,
  chaque route de l’API demande une clé secrète.
* Les **clés de test** fonctionnent dès l’inscription. Les **clés de production** fonctionnent une
  fois votre compte activé.

La clé secrète de test de votre bac à sable s’affiche une seule fois, à l’inscription. Les clés
suivantes se créent sur la page Clés d’API du tableau de bord, et une clé secrète ne s’affiche qu’une
fois, à sa création : conservez-la à ce moment-là.

## Envoyer votre première requête

Placez votre clé secrète de test dans une variable d’environnement, puis demandez à l’API qui vous
êtes. Envoyez la clé comme jeton *bearer*, ou comme nom d’utilisateur de l’authentification HTTP
Basic avec un mot de passe vide.

La réponse est votre compte : son identifiant, son statut d’activation (`sandbox` jusqu’à
l’activation) et la version de l’API qu’il utilise.

```bash
curl https://pay-api.adpromax.com/v1/account \
  -u "$APX_SECRET_KEY:"
```

```js title="account.mjs"
const response = await fetch('https://pay-api.adpromax.com/v1/account', {
  headers: { Authorization: `Bearer ${process.env.APX_SECRET_KEY}` },
})
const account = await response.json()
if (!response.ok)
  throw new Error(account.error.message)
console.log(account.id, account.activation_status)
```

```ts title="account.ts"
interface Account {
  id: string
  object: 'account'
  business_name: string
  activation_status: string
  api_version: string
}

interface ApiError {
  error: { type: string, code: string, message: string, request_id: string }
}

const response = await fetch('https://pay-api.adpromax.com/v1/account', {
  headers: { Authorization: `Bearer ${process.env.APX_SECRET_KEY}` },
})
if (!response.ok) {
  const { error } = (await response.json()) as ApiError
  throw new Error(`${error.code}: ${error.message}`)
}
const account = (await response.json()) as Account
console.log(account.id, account.activation_status)
```

```python title="account.py"
import os

import requests

response = requests.get(
    "https://pay-api.adpromax.com/v1/account",
    auth=(os.environ["APX_SECRET_KEY"], ""),
)
account = response.json()
if not response.ok:
    raise RuntimeError(account["error"]["message"])
print(account["id"], account["activation_status"])
```

```php title="account.php"
<?php
$ch = curl_init('https://pay-api.adpromax.com/v1/account');
curl_setopt_array($ch, [
    CURLOPT_USERPWD => getenv('APX_SECRET_KEY') . ':',
    CURLOPT_RETURNTRANSFER => true,
]);
$account = json_decode(curl_exec($ch), true);
if (curl_getinfo($ch, CURLINFO_RESPONSE_CODE) >= 400) {
    throw new RuntimeException($account['error']['message']);
}
echo $account['id'], ' ', $account['activation_status'], PHP_EOL;
```

```ruby title="account.rb"
require 'json'
require 'net/http'

uri = URI('https://pay-api.adpromax.com/v1/account')
request = Net::HTTP::Get.new(uri)
request.basic_auth(ENV.fetch('APX_SECRET_KEY'), '')

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http|
  http.request(request)
end
account = JSON.parse(response.body)
raise account.dig('error', 'message') unless response.is_a?(Net::HTTPSuccess)

puts "#{account['id']} #{account['activation_status']}"
```

```go title="account.go"
package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"os"
)

type Account struct {
	ID               string `json:"id"`
	BusinessName     string `json:"business_name"`
	ActivationStatus string `json:"activation_status"`
	APIVersion       string `json:"api_version"`
}

type APIError struct {
	Error struct {
		Code    string `json:"code"`
		Message string `json:"message"`
	} `json:"error"`
}

func main() {
	req, err := http.NewRequest(http.MethodGet, "https://pay-api.adpromax.com/v1/account", nil)
	if err != nil {
		log.Fatal(err)
	}
	req.SetBasicAuth(os.Getenv("APX_SECRET_KEY"), "")

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		log.Fatal(err)
	}
	defer res.Body.Close()

	if res.StatusCode >= 400 {
		var apiErr APIError
		json.NewDecoder(res.Body).Decode(&apiErr)
		log.Fatal(apiErr.Error.Message)
	}
	var account Account
	if err := json.NewDecoder(res.Body).Decode(&account); err != nil {
		log.Fatal(err)
	}
	fmt.Println(account.ID, account.ActivationStatus)
}
```

```java title="Account.java"
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class Account {
    public static void main(String[] args) throws Exception {
        HttpRequest request = HttpRequest.newBuilder(URI.create("https://pay-api.adpromax.com/v1/account"))
            .header("Authorization", "Bearer " + System.getenv("APX_SECRET_KEY"))
            .GET()
            .build();
        HttpResponse<String> response = HttpClient.newHttpClient()
            .send(request, HttpResponse.BodyHandlers.ofString());
        if (response.statusCode() >= 400) {
            throw new IllegalStateException(response.body());
        }
        // The account as JSON: read it with your JSON library (Jackson, Gson…).
        System.out.println(response.body());
    }
}
```

```csharp title="Account.cs"
using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text.Json;

var json = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower };
using var http = new HttpClient();
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
    "Bearer", Environment.GetEnvironmentVariable("APX_SECRET_KEY"));

using var response = await http.GetAsync("https://pay-api.adpromax.com/v1/account");
if (!response.IsSuccessStatusCode)
{
    var failure = await response.Content.ReadFromJsonAsync<ApiError>(json);
    throw new InvalidOperationException(failure?.Error.Message);
}
var account = await response.Content.ReadFromJsonAsync<Account>(json);
Console.WriteLine($"{account!.Id} {account.ActivationStatus}");

record Account(string Id, string BusinessName, string ActivationStatus, string ApiVersion);
record ApiError(ApiErrorBody Error);
record ApiErrorBody(string Code, string Message);
```

```rust title="main.rs"
// Cargo.toml: reqwest = { version = "0.12", features = ["blocking", "json"] }
//             serde = { version = "1", features = ["derive"] }
use serde::Deserialize;

#[derive(Deserialize)]
struct Account {
    id: String,
    activation_status: String,
}

#[derive(Deserialize)]
struct ApiError {
    error: ErrorBody,
}

#[derive(Deserialize)]
struct ErrorBody {
    message: String,
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let response = reqwest::blocking::Client::new()
        .get("https://pay-api.adpromax.com/v1/account")
        .bearer_auth(std::env::var("APX_SECRET_KEY")?)
        .send()?;
    if !response.status().is_success() {
        let failure: ApiError = response.json()?;
        return Err(failure.error.message.into());
    }
    let account: Account = response.json()?;
    println!("{} {}", account.id, account.activation_status);
    Ok(())
}
```

```json title="200 OK" response
{
  "id": "org_01k7cz9q3m8v6t2x4b5n7p1r0s",
  "object": "account",
  "business_name": "Atlas Shop",
  "country": "MA",
  "default_currency": "mad",
  "activation_status": "sandbox",
  "capabilities": {
    "card_payments": "inactive",
    "cash_payments": "inactive"
  },
  "api_version": "2026-11-01",
  "created": 1790847000
}
```

## Quand une clé est refusée

L’API répond `401` ou `403` avec un objet d’erreur ; son `code` dit pourquoi.

| Statut | `code`                    | Ce qui s’est passé                                                                  |
| ------ | ------------------------- | ----------------------------------------------------------------------------------- |
| 401    | `api_key_missing`         | La requête ne portait aucune clé.                                                   |
| 401    | `api_key_invalid`         | La clé n’existe pas. Vérifiez qu’il ne manque pas un caractère.                     |
| 401    | `api_key_revoked`         | La clé a été révoquée. Utilisez-en une autre.                                       |
| 401    | `api_key_expired`         | La clé a été renouvelée et son chevauchement est terminé. Utilisez la nouvelle clé. |
| 403    | `secret_key_required`     | Une clé publiable a servi là où il faut une clé secrète.                            |
| 403    | `account_inactive`        | Le compte est suspendu ou fermé.                                                    |
| 403    | `live_mode_not_activated` | Une clé de production a servi avant l’activation du compte.                         |

Le `message` de l’erreur l’explique dans la langue de votre requête, et son `request_id` identifie
la requête si vous écrivez au support.

## Garder les clés secrètes secrètes

* Lisez la clé depuis une variable d’environnement ou un coffre de secrets ; ne la versionnez
  jamais dans un dépôt.
* Ne placez jamais une clé secrète dans une page web, une application mobile ou un ticket de support.
* Si une clé secrète a pu fuiter, révoquez-la sur le tableau de bord et créez-en une nouvelle.

Renouvellement, révocation et le reste : [Sécurité](https://pay-docs.adpromax.com/fr/account/security).
