En vous promenant sur Beamreactor, nous stockons votre IP 48h pour des raisons de sécurité.

Lecteur Markdown

payment Documentation › PAYMENT_DOCUMENTATION_FR

PAYMENT_DOCUMENTATION_FR

Plugin : Payment #

Page de retour de paiement (succès, annulation, attente). Réceptionne l'utilisateur après checkout Stripe / PayPal / virement / chèque, vérifie le statut, affiche la confirmation et envoie l'email récapitulatif.

---

Rôle #

Le plugin `payment` n'initie aucun paiement — il reçoit les retours :

| Provider | URL de retour | Action |

|----------|---------------|--------|

| Stripe | `?obj=payment.php&order=NUM&session_id=cs_...` | Vérification synchrone de la session, marque l'order `paid` si Stripe confirme |

| PayPal | `?obj=payment.php&order=NUM&token=...` | Capture du paiement via `Payment::handleCallback('paypal', …)` |

| Virement | `?obj=payment.php&order=NUM` | Affiche IBAN/BIC + référence commande |

| Chèque | `?obj=payment.php&order=NUM` | Affiche l'adresse postale + libellé |

| Annulation | `?obj=payment.php&action=cancel&order=NUM` | Restaure le panier, redirige vers `products_checkout.php` |

L'initiation des paiements est gérée en amont par `products_checkout` (panier classique), `abo_checkout` (premier abonnement) et `reabo_checkout` (réabonnement).

---

Architecture HT / TTC #

| Couche | Format |

|--------|--------|

| `products.price`, `products.subscription_price` (DB) | HT |

| `order_items.unit_price_ht`, `unit_price_recurring_ht` (DB) | HT |

| Affichage utilisateur (panier, fiche, paiement) | TTC |

| API Stripe/PayPal `unit_amount` (cents) | TTC (calculé via `vat_rate`) |

| Saisie admin éditeur | TTC via toggle (mais stocké HT) |

| B2B VIES zero-rated | passer `vat_rate=0` au moment du checkout |

Tous les montants envoyés aux providers sont calculés via `bcmul(number_format(HT × (1 + vat/100), 2, '.', ''), '100', 0)` pour éviter les arrondis flottants.

---

Flux Stripe — vérification synchrone au retour #

Stripe redirige sur `success_url` avant que le webhook n'ait toujours fini de traiter. Sans vérification synchrone, l'utilisateur voyait "paiement en cours de traitement" alors que tout était OK.

Stripe Checkout → success_url (payment.php?session_id=...)
        ↓
payment.php : Payment::verifySession('stripe', $sessionId, $orderId)
        ↓
PaymentStripe::verifySession()
   GET /v1/checkout/sessions/{id}
   Vérifie : status='complete' ET payment_status IN ('paid','no_payment_required')
        ↓
Si OK → Payment::updateStatus($orderId, 'paid', $paymentIntent, 'Stripe session verified on return')
        ↓
Affichage de la confirmation

Le webhook reste la source de vérité de secours (idempotent grâce à `Payment::updateStatus`). Si la vérification synchrone échoue (réseau, Stripe lent), le webhook prendra le relais et l'utilisateur verra "en cours de traitement" puis pourra rafraîchir.

---

Affichage du total avec abonnement #

Pour les commandes mixtes (produits one-time + abonnement), `orders.total_ttc` ne contient que la part one-time. Le premier versement de l'abonnement est facturé par Stripe via une `line_item` séparée.

L'affichage calcule donc :

$paidTodayTtc = (float)$order['total_ttc'] + $recurringTtc;

avec `$recurringTtc = Σ (unit_price_recurring_ht × (1 + vat_rate/100) × quantity)` sur les `order_items` où `is_subscription = 1`.

Trois lignes affichées :

  • Total payé aujourd'hui : `$paidTodayTtc`
  • Dont produits : `$order['total_ttc']`
  • Dont premier versement abonnement : `$recurringTtc`
  • Puis : `$recurringTtc / mois` (récurrence)

---

Email de confirmation #

Envoyé une seule fois par commande (flag `orders.email_sent`). Contient :

  • Récapitulatif lignes (nom, qty, ligne TTC)
  • Sous-total HT + TVA + total TTC
  • Adresses facturation/livraison
  • Instructions virement/chèque si applicable
  • Référence transaction Stripe/PayPal si applicable

Expédition via `mail()` natif PHP, expéditeur `$cfg[34]['headoffice_name'] <$cfg[11]['commercial']>`.

---

Annulation #

Si `?action=cancel` ET `payment_status='unpaid'` :

1. Récupère les `order_items` de l'order

2. Vide le panier courant (`Panier::vider()`)

3. Réinjecte les items (`Panier::ajouterProduit(productId, quantity)`)

4. Marque l'order `failed` via `Payment::updateStatus(..., 'failed', ..., 'Payment cancelled by user')`

5. Redirige vers `products_checkout.php`

Même logique si l'utilisateur revient sur `payment.php` avec un order déjà en `failed` (recovery panier).

---

Webhook — `handlers/payment.mod.php` #

Endpoint : `?obj=payment.mod.php&method=stripe` (ou `paypal`)

POST raw payload
  ↓
$rawPayload = file_get_contents('php://input')
$method     = $_GET['method']
  ↓
Payment::handleCallback($method, ['_raw'=>…, '_headers'=>…, 'event'=>…])
  ↓
Provider vérifie signature + traite l'event
  ↓
HTTP 200 {status:ok} | 400 {status:error}

Stripe et PayPal doivent être configurés dans leurs dashboards respectifs avec ces URLs :

  • `https://shop.example.com/index.php?obj=payment.mod.php&method=stripe`
  • `https://shop.example.com/index.php?obj=payment.mod.php&method=paypal`

---

Configuration `cog.php` #

| Indice | Rôle |

|--------|------|

| `$cfg[34]` | Coordonnées société (nom, adresse, SIRET) — utilisées dans l'email |

| `$cfg[35]` | IBAN / BIC pour les virements |

| `$cfg[36]` | Stripe : `public_key`, `secret_key`, `webhook_secret`, `mode` |

| `$cfg[37]` | PayPal : `client_id`, `client_secret`, `webhook_id`, `mode` |

| `$cfg[38]` | Chèque : `order_to` (destinataire du chèque) |

| `$cfg[11]['commercial']` | Email expéditeur des confirmations |

---

Dépendances #

  • `Beamreactor\Database\SQL`
  • `Beamreactor\Payment\Payment` — façade publique (`processSubscription`, `verifySession`, `handleCallback`, `updateStatus`, `getInstructions`)
  • `Beamreactor\Payment\PaymentStripe`, `PaymentPaypal`, `PaymentVirement`, `PaymentCheque` — handlers concrets, autoloadés par `Payment::loadHandler()` (include explicite, pas par namespace)
  • `Beamreactor\Shop\Panier` — restauration panier sur annulation/échec
  • `Beamreactor\Sanitizer\Parser` — sanitize des paramètres GET
  • Fonction PHP `mail()` pour l'envoi de l'email

⚠️ Ne jamais instancier `PaymentStripe` directement par `new \Beamreactor\Payment\PaymentStripe()` — la classe n'est pas autoloadée par namespace. Toujours passer par les méthodes statiques de `Payment::`.

---

Tables SQL #

  • `orders` : commande principale (`payment_status`, `total_ttc`, `payment_method`, `email_sent`, …)
  • `order_items` : lignes commande, dont colonnes abonnement (`is_subscription`, `unit_price_recurring_ht`, `subscription_billing_anchor`, `subscription_interval`)
  • `payment_subscriptions` : abonnements actifs Stripe/PayPal (alimentée par les webhooks)

---

Installation #

Déposer le répertoire `payment/` dans `/plugins/`. Aucune migration spécifique. Les tables `orders` / `order_items` sont gérées par le module shop principal.

de en fr