# Le paiement direct

Votre formulaire, votre écran : votre serveur envoie le numéro du payeur, Payiz lance la demande, le payeur valide sur son téléphone. Personne ne quitte votre application.

## Quand le choisir

- **[La page de paiement](https://docs.payiz.app/page-de-paiement)** : Payiz dessine tout. Vous envoyez le lien, ou vous y renvoyez le client. (`payment_url`)
- **[Le widget](https://docs.payiz.app/widget)** : La fenêtre de Payiz, posée dans votre page. Le client ne la quitte pas. (`payiz.js`)
- **Le paiement direct** : Vous dessinez tout. Payiz lance la demande sur le téléphone et vous dit la suite. (`payer`)

Les trois créent le même paiement : mêmes frais, mêmes webhooks, même reçu. Vous pouvez les mêler — le lien pour vos ventes sur les réseaux, le direct dans votre application.

## Le parcours

1. **Votre écran** recueille le numéro et l’opérateur
2. **Votre serveur** appelle Payiz avec sa clé secrète
3. **Le payeur** valide sur son téléphone, avec son code
4. **Le webhook** vous dit que c’est payé, ou pourquoi non

## Lancer la demande

Un seul appel : le paiement, et le numéro qui le paie. La clé porte le geste **Paiements · lancer**, et l’appel un en-tête `Idempotency-Key`, obligatoire : tirez-le de votre commande, deux clics donnent alors un seul paiement.

```bash
curl https://api.payiz.app/v1/payments \
  -H "Authorization: Bearer sk_test_…" \
  -H "Idempotency-Key: cmd-42" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 10000,
    "currency": "XOF",
    "reference": "cmd-42",
    "payer": {
      "phone": "+2290197000000",
      "operator": "mtn"
    }
  }'
```

La réponse arrive tout de suite ; en voici l’essentiel. Le paiement est `processing` : la demande est partie, le payeur ne l’a pas encore validée.

```json
{
  "object": "payment",
  "id": "pay_01j9x3kq7m4tzv8qhw2n",
  "amount": 10000,
  "currency": "XOF",
  "status": "processing",
  "reference": "cmd-42",
  "latest_attempt": {
    "object": "payment_attempt",
    "id": "tnt_01j9x4a2b7c5d8e3f6g1h0jk",
    "status": "pending",
    "source": "api",
    "phone": "+229 01 •• •• 34 56",
    "operator": "mtn",
    "failure_code": null,
    "payer_message": "Validez le paiement de 10 000 F CFA sur votre téléphone. Si rien n’arrive, vérifiez votre solde et votre réseau, puis réessayez."
  },
  "payment_url": "https://pay.payiz.app/pay_01j9x3kq7m4tzv8qhw2n",
  "livemode": false
}
```

> Sans `operator`, Payiz le déduit du numéro. Quand plusieurs opérateurs sont possibles, l’appel échoue avec `operator_required` et la liste des candidats dans `details.operators` : proposez-les au payeur. [GET /v1/payment_methods](https://docs.payiz.app/api/moyens/lister) vous donne les opérateurs, leurs logos et leurs bornes.

## Afficher la consigne

Pendant l’attente, dites au payeur quoi faire, et laissez-lui changer de numéro.

Votre écran, pendant l’attente : « Validez sur votre téléphone », « Validez le paiement de 10 000 F CFA sur votre téléphone. », et un bouton « Changer de numéro ».

`payer_message` est écrit pour le payeur : la consigne pendant l’attente, puis l’issue. Vous pouvez aussi écrire le vôtre à partir de `failure_code`.

## Attendre l’issue

Le webhook fait foi. `payment.succeeded` dit que l’argent est arrivé ; `payment.attempt_failed` dit pourquoi la demande n’a pas abouti. Ne concluez jamais sur la réponse immédiate.

```json
{
  "id": "evt_01j9x3p5hd2m8nq6tb0c",
  "type": "payment.attempt_failed",
  "livemode": false,
  "data": {
    "object": {
      "object": "payment",
      "id": "pay_01j9x3kq7m4tzv8qhw2n",
      "status": "requires_payment",
      "latest_attempt": {
        "status": "failed",
        "failure_code": "insufficient_funds",
        "payer_message": "Le solde ne suffit pas. Rechargez, ou payez avec un autre numéro."
      }
    }
  }
}
```

Sans webhook, relisez le paiement avec [GET /v1/payments/{id}](https://docs.payiz.app/api/paiements/lire) : `latest_attempt` y porte la même raison.

## Relancer

Après un échec, le paiement reste payable jusqu’à `expires_at`. Relancez sur le même paiement, avec le même numéro ou un autre : jamais deux demandes à la fois — une demande qui attend encore répond `attempt_in_progress`.

```bash
curl https://api.payiz.app/v1/payments/pay_01j9x3kq7m4tzv8qhw2n/attempts \
  -H "Authorization: Bearer sk_test_…" \
  -H "Idempotency-Key: cmd-42-relance-1" \
  -H "Content-Type: application/json" \
  -d '{"phone":"+2290196000000","operator":"moov"}'
```

## Les raisons d’un échec

Un code stable dans `failure_code`, et une phrase prête dans `payer_message`.

| failure_code | Ce qui s’est passé | Ce que votre écran propose |
| --- | --- | --- |
| `insufficient_funds` | Solde insuffisant | Un autre numéro, ou relancer plus tard |
| `payer_declined` | Le payeur refuse | Relancer s’il le demande |
| `timeout` | Pas de validation à temps | Relancer |
| `invalid_phone` | Numéro inconnu | Corriger le numéro |
| `operator_limit` | Plafond de l’opérateur | Un autre numéro ou un autre montant |
| `operator_unavailable` | Opérateur injoignable | Réessayer plus tard, ou un autre opérateur |
| `declined` | Refusé | Ne pas insister |
| `unknown` | Autre réponse | Relancer |

## Bonnes habitudes

- **La clé secrète vit sur votre serveur.** Votre application parle à votre serveur, jamais à Payiz.
- **Restreignez la clé** aux adresses IP de vos serveurs.
- **Une Idempotency-Key tirée de la commande** : un rejeu ne relance jamais le téléphone.
- **Livrez sur payment.succeeded**, et sur rien d’autre.
- **Une demande à la fois** : attendez son issue avant de relancer.

## Passer au réel

En test, tous les espaces peuvent lancer des demandes : les deux derniers chiffres du numéro choisissent l’issue (voir [Tester](https://docs.payiz.app/simulateur)). En réel, le direct s’ouvre avec votre palier, et votre espace vous dit s’il l’est ; [Passer au réel](https://docs.payiz.app/passer-au-reel) dit le reste.

