Tech · · 3 min

Introduire le paiement Stripe dans vos projets PHP

Implémentez le paiement par carte avec Stripe PaymentIntent (conforme SCA) dans un projet PHP sur-mesure, sans framework.

Introduire le paiement Stripe dans vos projets PHP

TL;DR

Un dépôt de code fonctionnel complet (avec simulation de panier) est disponible. Voici un exemple simple pour implémenter Stripe PaymentIntent (conforme SCA) dans votre projet et accepter les paiements.

Introduction

Si vous développez des sites, des SaaS ou des applications web, vous avez probablement eu besoin de fonctionnalités de paiement. Sur un CMS, des extensions (WooCommerce pour WordPress, Commerce pour Drupal) gèrent le paiement pour vous. Hors de ces solutions, vous devrez implémenter votre propre système. Il existe de nombreuses passerelles (PayPal, Apple Pay, Six Payment) et des agrégateurs comme Omnipay. Pour rester simple, nous nous concentrons ici sur Stripe et le paiement par carte.

Implémentation de Stripe

Inscrivez-vous sur Stripe pour obtenir vos clés secrète et publique, et rendez-les accessibles à votre application.

Veuillez NE PAS enregistrer les cartes de vos clients dans votre application ou base de données. Envoyez les données de carte DIRECTEMENT à Stripe. N'enregistrez JAMAIS les informations de carte.

Front

Créez un formulaire contenant les informations souhaitées et le champ carte :

<form id="payment-form" data-stripe-public-key="<?= STRIPE_PUBLIC_KEY ?>">
  <div class="sr-combo-inputs-row">
    <div class="sr-input sr-card-element" id="card-element"></div>
    <div id="card-errors"></div>
  </div>
  <button id="submit">
    <div class="spinner hidden" id="spinner"></div>
    <span id="button-text">Payer
      <span id="order-amount"><?= $price ?> <?= STRIPE_CURRENCY_SYMBOL ?></span>
    </span>
  </button>
</form>

L'élément principal est #card-element, géré par Stripe.js pour afficher les champs de carte. Incluez le script Stripe dans votre balise head :

<head>
  <script src="https://js.stripe.com/v3/"></script>
</head>

Ensuite, un fichier script.js (chargé en fin de body) monte les comportements de carte Stripe, écoute les informations de carte et la soumission du formulaire. La partie importante est l'appel à la fonction pay(), qui démarre le paiement à la soumission. Elle s'appuie sur quelques fonctions utilitaires : changeLoadingState() (spinner), showError() (erreurs), handleAction() (étape supplémentaire demandée par la banque, via stripe.handleCardAction) et orderComplete() (succès). Quand une validation supplémentaire est requise (3D Secure), handleAction() resoumet la requête au même endpoint /pay.php avec le paymentIntentId créé à la première étape.

Retour

Installez le SDK PHP de Stripe, puis créez quelques fichiers :

  • public/pay.php : l'endpoint qui reçoit les demandes de paiement
  • src/StripeHelper.php : classe d'aide pour les étapes du paiement
  • src/BodyException.php : exception levée si le corps de requête est mal formaté

L'endpoint pay.php ne renvoie que du JSON, n'autorise que les requêtes POST, définit la clé secrète, récupère le panier et son montant total (en centimes), lit le corps de la requête, construit le PaymentIntent et renvoie la réponse, le tout dans un bloc try/catch.

La construction du PaymentIntent :

public static function createPaymentIntent(object $body, int $amount): PaymentIntent
{
    // Nouveau PaymentIntent (première étape, depuis pay())
    if (!empty($body->paymentMethodId)) {
        $intent = PaymentIntent::create([
            'amount' => $amount,
            'currency' => STRIPE_CURRENCY,
            'payment_method' => $body->paymentMethodId,
            'confirmation_method' => 'manual',
            'confirm' => true,
        ]);
    } else if (!empty($body->paymentIntentId)) {
        // Confirmation (depuis handleAction(), après 3D Secure)
        $intent = PaymentIntent::retrieve($body->paymentIntentId);
        $intent->confirm();
    }
    return $intent;
}

Dans la première condition, on crée un nouveau PaymentIntent. Si la banque ne demande pas de validation, le paiement réussit immédiatement. Sinon (3D Secure ou validation en deux étapes, le protocole SCA), le front passe par handleAction() et resoumet avec le paymentIntentId, qui tombe dans la seconde condition pour être confirmé.

La réponse renvoyée au client dépend du statut de l'intention :

public static function generateResponse(PaymentIntent $intent): array
{
    switch ($intent->status) {
        case PaymentIntent::STATUS_REQUIRES_ACTION:
        case 'requires_source_action':
            return [
                'requiresAction' => true,
                'paymentIntentId' => $intent->id,
                'clientSecret' => $intent->client_secret,
            ];
        case PaymentIntent::STATUS_REQUIRES_PAYMENT_METHOD:
        case 'requires_source':
            return ['error' => 'Votre carte a été refusée, veuillez fournir un nouveau mode de paiement'];
        case PaymentIntent::STATUS_SUCCEEDED:
            return ['clientSecret' => $intent->client_secret];
        case PaymentIntent::STATUS_CANCELED:
            return ['error' => 'Le paiement a été annulé, veuillez réessayer plus tard'];
        default:
            return ['error' => 'Une erreur s\'est produite, veuillez réessayer plus tard'];
    }
}

La clé requiresAction déclenche l'appel à handleAction() côté front.

Solution

Ce tutoriel est volontairement simple pour démarrer avec l'API Stripe et bâtir votre propre implémentation. Aucun framework front ou back n'a été utilisé, pour rester en PHP/JS clair. Stripe permet bien d'autres choses : paiements récurrents, sauvegarde des clients, envoi de factures. L'exemple officiel de Stripe (https://github.com/stripe-samples/accept-a-card-payment) a servi de base à cette solution. N'hésitez pas à me faire part de vos commentaires ou corrections. Merci de votre lecture.