Skip to content

Paiement en ligne & Enregistrement de carte

Resamania met à disposition un widget de paiement universel qui abstrait la complexité des différents PSP en une interface unique. Ce widget prend en charge aussi bien le paiement one-shot (règlement d'une vente) que l'enregistrement de carte (Wallet).

NOTE

Une intégration legacy (spécifique à chaque PSP) est disponible pour les partenaires déjà en production avec cette approche. Consultez la documentation legacy si nécessaire.

PSP supportés

PSPOne-shotWallet
Stripe
Payline
GlobalPayment
XplorPay / Snap

Modes d'intégration

Deux approches sont disponibles selon votre stack technique :

ModeDescriptionRecommandé pour
iframe nativeLe widgetUrl retourné par l'API s'intègre directement dans une <iframe> HTML. La communication (succès/échec) passe par window.postMessage.Tout projet web avec un peu de JS
Redirection directeRedirection HTTP vers la page de paiement du PSP — votre backend redirige l'utilisateur.Stack sans JS côté client (PHP, etc.)
Composant React<WidgetIframe /> via @resamania/payment-react — encapsule l'iframe et expose des callbacks onActionSuccess / onActionFailure. Le package s'installe via URL HTTP.Applications React / Next.js

IMPORTANT

Le mode utilisé est déterminé par la présence ou l'absence de validUrl / cancelUrl dans le corps de la requête POST /payable_objects :

  • Sans validUrl ni cancelUrl → l'API génère un widgetUrl à intégrer en iframe
  • Avec validUrl et cancelUrl → l'API retourne un redirectUrl vers le PSP

Cas 1 : Paiement en ligne (one-shot)

Flux — Mode widget (React)

1. Votre backend  →  POST /{clientToken}/payable_objects   (sans validUrl ni cancelUrl)
                 ←  { id, widgetUrl, ... }

2. Votre frontend →  <WidgetIframe src={widgetUrl} />

3. L'utilisateur paie dans l'iframe

4.               ←  onActionSuccess(payload, { provider })
                     ou onActionFailure(payload, { provider })

Flux — Mode redirection (PHP, etc.)

1. Votre backend  →  POST /{clientToken}/payable_objects   (avec validUrl et cancelUrl)
                 ←  { id, redirectUrl, ... }

2. Votre backend  →  HTTP 302 vers redirectUrl

3. L'utilisateur paie sur la page du PSP

4. Le PSP         →  Redirige vers validUrl (succès ou échec) ou cancelUrl (abandon)

5. Votre backend  →  PUT /{clientToken}/payable_objects/{id}/check
                 ←  { state: 'validated' | 'refused' | ... }

1. Créer un objet de paiement

POST /{clientToken}/payable_objects

Mode widget — ne pas envoyer validUrl ni cancelUrl :

json
{
  "contactId":      "/demoapi/contacts/2972926",
  "amount":         2500,
  "currency":       "EUR",
  "clubCode":       "CFF",
  "clubId":         "/demoapi/clubs/1398",
  "checkout":       "/demoapi/checkouts/1361",
  "payableObjects": {
    "sales":     ["/demoapi/sales/3558464"],
    "invoices":  [],
    "incidents": []
  },
  "useWebWallet": true
}

Mode redirection — envoyer validUrl et cancelUrl :

json
{
  "contactId":      "/demoapi/contacts/2972926",
  "amount":         2500,
  "currency":       "EUR",
  "clubCode":       "CFF",
  "clubId":         "/demoapi/clubs/1398",
  "checkout":       "/demoapi/checkouts/1361",
  "payableObjects": {
    "sales":     ["/demoapi/sales/3558464"],
    "invoices":  [],
    "incidents": []
  },
  "validUrl":  "https://votre-site.com/payment/return",
  "cancelUrl": "https://votre-site.com/payment/cancel"
}

WARNING

La validUrl est appelée aussi bien en cas de succès qu'en cas d'échec. Intégrez votre propre identifiant de session dans cette URL (ex. ?sessionId=xxx) pour retrouver la transaction lors du retour — Resamania n'y ajoute aucun paramètre automatiquement.

Champs retournés selon le mode :

ChampMode widgetMode redirectionDescription
idIRI du payableObject (ex. /demoapi/payable_objects/12345)
widgetUrlURL à passer au composant <WidgetIframe> — contient le token d'authentification
redirectUrlURL vers la page de paiement du PSP
tokenRéférence de la transaction côté PSP

2a. Afficher le widget (mode React)

Installez le package :

bash
npm install "https://payment.resamania.com/pkg/resamania-payment-react-0.0.0.tgz"
tsx
import { WidgetIframe } from '@resamania/payment-react'

function PaymentPage({ widgetUrl }) {
  const handleSuccess = (payload, { provider }) => {
    // Le paiement a réussi côté PSP.
    // Vérifiez toujours l'état côté serveur avant de confirmer la commande.
    console.log('Paiement accepté via', provider, payload)
  }

  const handleFailure = (payload, { provider }) => {
    console.error('Paiement refusé via', provider, payload)
  }

  return (
    <WidgetIframe
      src={widgetUrl}
      style={{ width: '100%', minHeight: 500, border: 'none' }}
      onActionSuccess={handleSuccess}
      onActionFailure={handleFailure}
    />
  )
}

NOTE

Le widgetUrl retourné par l'API contient déjà le token d'authentification (autologintoken) en query param. Utilisez-le directement sans le modifier.

2b. Redirection directe (mode PHP / server-side)

php
// Récupérer le redirectUrl dans la réponse et rediriger
header('Location: ' . $payableObject['redirectUrl']);
exit;

3. Vérifier l'état du paiement

PUT /{clientToken}/payable_objects/{id}/check

À appeler côté serveur après un retour de paiement (redirection) ou pour confirmer un succès signalé par le widget. Interroge le PSP en temps réel et met à jour l'état de l'objet.

WARNING

{id} est l'identifiant numérique du payableObject (ex. 12345), extrait de l'IRI /demoapi/payable_objects/12345.

ÉtatDescription
createdCréé, pas encore présenté à l'utilisateur
pendingEn attente de confirmation du PSP
requestedPaiement en cours de traitement
validatedPaiement accepté ✅
refusedPaiement refusé ❌
canceledTransaction annulée par l'utilisateur

Cas 2 : Enregistrement de carte (Webcard)

Le widget peut enregistrer une carte de paiement sans débit immédiat — utile pour les mandats SEPA, les abonnements récurrents ou le pré-enregistrement avant achat.

Flux

1. Votre backend  →  POST /{clientToken}/web_wallets
                 ←  { widgetUrl, ... }

2. Votre frontend →  <WidgetIframe src={widgetUrl} />

3. L'utilisateur entre sa carte

4.               ←  onActionSuccess(WebCard[], { provider })
                     ou onActionFailure(payload, { provider })

1. Créer un portefeuille

POST /{clientToken}/web_wallets

json
{
  "contactId": "/demoapi/contacts/2972926",
  "clubId":    "/demoapi/clubs/1398",
  "checkout":  "/demoapi/checkouts/1361"
}

La réponse contient un widgetUrl à passer directement au composant <WidgetIframe />.

2. Afficher le formulaire d'enregistrement

tsx
import { WidgetIframe } from '@resamania/payment-react'

function WebCardPage({ widgetUrl }) {
  const handleSuccess = (webCards, { provider }) => {
    // webCards est un tableau de WebCard enregistrées
    // [{ id, cardReference, state, createdAt, ... }]
  }

  return (
    <WidgetIframe
      src={widgetUrl}
      style={{ width: '100%', minHeight: 400, border: 'none' }}
      onActionSuccess={handleSuccess}
      onActionFailure={(payload, { provider }) => console.error(payload)}
    />
  )
}

Référence du composant WidgetIframe

PropTypeDescription
srcstringwidgetUrl retourné par l'API (requis)
styleCSSPropertiesStyles inline de l'iframe
classNamestringClasse CSS de l'iframe
onActionSuccess(payload, context) => voidCallback de succès
onActionFailure(payload, context) => voidCallback d'échec

context contient : { provider: string } — le PSP utilisé par le club.


Exemples complets

Des exemples complets sont disponibles dans le repository resamania2-partners :


Migration depuis l'intégration legacy

Si vous utilisez l'ancienne intégration PSP-spécifique (Stripe Elements, Payline redirect…), voici les changements :

Avant (intégration legacy)Maintenant (Widget Resamania)
Intégration spécifique à chaque PSPUn seul widget, tous PSP
Gestion de clientSecret + publishableKey (Stripe)Un seul champ widgetUrl retourné par l'API
@stripe/react-stripe-js@resamania/payment-react
Callbacks PSP natifsonActionSuccess / onActionFailure
Pas de vérification unifiéePUT /payable_objects/{id}/check