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
| PSP | One-shot | Wallet |
|---|---|---|
| Stripe | ✅ | ✅ |
| Payline | ✅ | ✅ |
| GlobalPayment | ✅ | ✅ |
| XplorPay / Snap | ✅ | ❌ |
Modes d'intégration
Deux approches sont disponibles selon votre stack technique :
| Mode | Description | Recommandé pour |
|---|---|---|
| iframe native | Le 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 directe | Redirection 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
validUrlnicancelUrl→ l'API génère unwidgetUrlà intégrer en iframe - Avec
validUrletcancelUrl→ l'API retourne unredirectUrlvers 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 :
{
"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 :
{
"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 :
| Champ | Mode widget | Mode redirection | Description |
|---|---|---|---|
id | ✅ | ✅ | IRI du payableObject (ex. /demoapi/payable_objects/12345) |
widgetUrl | ✅ | ❌ | URL à passer au composant <WidgetIframe> — contient le token d'authentification |
redirectUrl | ✅ | ✅ | URL vers la page de paiement du PSP |
token | ✅ | ✅ | Référence de la transaction côté PSP |
2a. Afficher le widget (mode React)
Installez le package :
npm install "https://payment.resamania.com/pkg/resamania-payment-react-0.0.0.tgz"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)
// 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.
| État | Description |
|---|---|
created | Créé, pas encore présenté à l'utilisateur |
pending | En attente de confirmation du PSP |
requested | Paiement en cours de traitement |
validated | Paiement accepté ✅ |
refused | Paiement refusé ❌ |
canceled | Transaction 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
{
"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
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
| Prop | Type | Description |
|---|---|---|
src | string | widgetUrl retourné par l'API (requis) |
style | CSSProperties | Styles inline de l'iframe |
className | string | Classe CSS de l'iframe |
onActionSuccess | (payload, context) => void | Callback de succès |
onActionFailure | (payload, context) => void | Callback 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 PSP | Un 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 natifs | onActionSuccess / onActionFailure |
| Pas de vérification unifiée | PUT /payable_objects/{id}/check |