Web SDK Payment
Description
Bibliothèque JavaScript pour afficher le formulaire de paiement sur la page du marchand. Cette méthode convient aux marchands ayant un niveau de conformité aux exigences PCI DCC élevé ou faible.
Scénario de fonctionnement :
- Le marchand enregistre la commande via REST API dans la passerelle de paiement
- Le
mdOrderobtenu (numéro de commande) est transmis par le marchand à la page où cette bibliothèque js est utilisée
Web SDK offre la possibilité d'ajouter sur sa page de paiement des champs de saisie de données de paiement via iframe directement depuis la passerelle de paiement. La sécurité de transmission des données est assurée par le chiffrement du protocole HTTPS.
Avantages :
- Sécurisé : La page de paiement du marchand et ses scripts n'ont pas accès aux champs de paiement transmis via iframe. Les données des cartes ne peuvent pas être collectées par des scripts externes.
- Simple : Le script s'intègre facilement sur la page. Pour correspondre au style général du site, une personnalisation minimale est requise.
- Fiable : Du côté du marchand, la conformité aux niveaux élevés PCI DCC n'est pas requise.
- Pratique : Des champs supplémentaires peuvent être transmis au serveur, tels que
email,language,phoneetjsonParams.
Inconvénients :
- Actuellement, Web SDK n'est pas compatible avec la fonction de tentatives multiples de paiement.
La bibliothèque aide à la collecte des données de carte, leur validation et vérification, la réalisation du paiement et la redirection automatique de l'acheteur vers la page finale via le returnUrl spécifié dans les paramètres.
Comment utiliser
Connecter le script
Environnement de test
<script src="https://dev.bpcbt.com/payment/modules/multiframe/main.js"></script>Environnement de production
<script src="https://dev.bpcbt.com/payment/modules/multiframe/main.js"></script>Préparation
Tout d'abord, il est nécessaire de créer un formulaire HTML pour accepter les paiements. Le formulaire doit contenir les blocs #pan, #expiry, #cvc et le bouton #pay. Il n'est pas obligatoire d'utiliser exactement ces noms de champs. Vous pourrez configurer les noms dont vous avez besoin lors de l'initialisation.
Exemple de formulaire HTML qui ne prend pas en charge les liaisons :
<div class="card-body">
<div class="col-12">
<label for="pan" class="form-label">Card number</label>
<!-- Container for card number field -->
<div id="pan" class="form-control"></div>
</div>
<div class="col-6 col-expiry">
<label for="expiry" class="form-label">Expiry</label>
<!-- Container for expiry card field -->
<div id="expiry" class="form-control"></div>
</div>
<div class="col-6 col-cvc">
<label for="cvc" class="form-label">CVC / CVV</label>
<!-- Container for CVC/CVV field -->
<div id="cvc" class="form-control"></div>
</div>
</div>
<!-- Pay button -->
<button class="btn btn-primary btn-lg" type="submit" id="pay">
<!-- Payment loader -->
<span class="spinner-border spinner-border-sm visually-hidden" id="pay-spinner"></span>
<span>Pay</span>
</button>
<!-- Container for errors -->
<div class="error my-2 text-center text-danger visually-hidden" id="error"></div>Si vous utilisez les liaisons, vous devez inclure un bloc HTML supplémentaire : #select-binding.
Voici un exemple de formulaire HTML qui prend en charge les liaisons :
<div class="card-body">
<div class="col-12" id="select-binding-container" style="display: none">
<!-- Select for bindings -->
<select class="form-select" id="select-binding" aria-label="Default select example">
<option selected value="new_card">Pay with a new card</option>
</select>
</div>
<div class="col-12">
<label for="pan" class="form-label">Card number</label>
<!-- Container for card number field -->
<div id="pan" class="form-control"></div>
</div>
<div class="col-6 col-expiry">
<label for="expiry" class="form-label">Expiry</label>
<!-- Container for expiry card field -->
<div id="expiry" class="form-control"></div>
</div>
<div class="col-6 col-cvc">
<label for="cvc" class="form-label">CVC / CVV</label>
<!-- Container for cvc/cvv field -->
<div id="cvc" class="form-control"></div>
</div>
<label class="col-12" id="save-card-container">
<!-- Save card checkbox -->
<input class="form-check-input" type="checkbox" value="" id="save-card" />
Save card
</label>
</div>
<!-- Pay button -->
<button class="btn btn-primary btn-lg" type="submit" id="pay">
<!-- Payment loader -->
<span class="spinner-border spinner-border-sm visually-hidden" id="pay-spinner"></span>
<span>Pay</span>
</button>
<!-- Container for errors -->
<div class="error my-2 text-center text-danger visually-hidden" id="error"></div>Vous pouvez ajouter au formulaire tous les champs supplémentaires, tels que Cardholder name (nom du porteur de carte), Email (courrier électronique), Phone (numéro de téléphone) etc. Cependant, n'oubliez pas de les transmettre ultérieurement dans la méthode doPayment().
Initialisation du Web SDK
Description du formulaire de paiement
Vous devez exécuter la fonction constructeur new window.PaymentForm().
window.PaymentForm() peut accepter les propriétés suivantes :
Propriétés d'initialisation PaymentForm
Par défaut, apiContext est automatiquement extrait du lien utilisé pour connecter le script
modules/multiframe/main.js.
Valeur par défaut :
en.
Valeur par défaut -
true
Par défaut
false
Par défaut
false
Par défaut
true
Par défaut
true
Valeur par défaut :
field-container
Par exemple :
onFormValidate: (isValid) => {
alert(isValid ? 'Congratulations!' : 'Oops! We regret.');
}Exemple d'initialisation Web SDK
const webSdkPaymentForm = new window.PaymentForm({
// Numéro de commande (l'enregistrement de commande se fait avant l'initialisation du formulaire)
mdOrder: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
// Nom de classe qui sera défini pour les conteneurs avec les champs de saisie
containerClassName: "field-container",
onFormValidate: (isValid) => {
// Traitement de la validation du formulaire
},
// Contexte pour les requêtes API
apiContext: "/payment",
// Langue - utilisée pour la localisation des erreurs et des noms de placeholders.
// La langue doit être supportée dans les paramètres du Marchand
language: "en",
// Basculer automatiquement le focus lors du remplissage des champs
autoFocus: true,
// Afficher l'icône du système de paiement
showPanIcon: true,
// Styles personnalisés pour l'icône du système de paiement
panIconStyle: {
height: "16px",
top: "calc(50% - 8px)",
right: "8px",
},
fields: {
pan: {
container: document.querySelector("#pan"),
onFocus: (containerElement) => {
// Action lors de l'obtention du focus par le champ
// (containerElement contient la référence à l'élément-conteneur du champ)
},
onBlur: (containerElement) => {
// Action lors de la perte du focus par le champ
// (containerElement contient la référence à l'élément-conteneur du champ)
},
onValidate: (isValid, containerElement) => {
// Action lors de la validation du champ
// (isValid est égal à true si le champ est valide, sinon false)
// (containerElement contient la référence à l'élément-conteneur du champ)
},
},
expiry: {
container: document.querySelector("#expiry"),
// ...
},
cvc: {
container: document.querySelector("#cvc"),
// ...
},
},
// Styles pour les champs de saisie
styles: {
// État de base
base: {
color: "black",
padding: '0px 16px',
fontSize: '18px',
fontFamily: 'monospace',
},
// État avec focus
focus: {
color: "blue",
},
// État désactivé
disabled: {
color: "gray",
},
// Avec valeur valide
valid: {
color: "green",
},
// Avec valeur invalide
invalid: {
color: "red",
},
// Style pour le placeholder
placeholder: {
// Style de base
base: {
color: "gray",
},
// Style lors du focus
focus: {
color: "transparent",
},
},
},
});Méthode destroy
La méthode destroy() dans Web SDK est utilisée pour supprimer toutes les ressources et les écouteurs d'événements liés à une instance spécifique de Web SDK. Lorsque vous appelez la méthode destroy(), elle nettoie tous les écouteurs d'événements et les conteneurs de champs de saisie qui ont été créés par Web SDK au cours de son cycle de vie. Ceci est utile lorsque vous n'avez plus besoin de l'instance Web SDK.
La méthode destroy() exécute généralement les tâches suivantes :
- Supprime tous les écouteurs d'événements qui ont été ajoutés à l'instance Web SDK.
- Nettoie tous les conteneurs de champs de saisie créés.
Exemple de méthode destroy
document.querySelector("#destroy").addEventListener("click", function () {
webSdkPaymentForm.destroy();
});Stylisation
La stylisation des conteneurs dans lesquels sont transmis les champs de saisie est définie de manière autonome selon le design de votre page. Différents états des conteneurs de champ de saisie peuvent être stylisés à l'aide des classes CSS suivantes :
-
{className}--focus- champ en focus -
{className}--valid- champ avec une valeur valide -
{className}--invalid- champ avec une valeur invalide
Le paramètre className est défini lors de l'initialisation via le paramètre containerClassName dans les propriétés window.PaymentForm().
Exemple :
<style>
.field-container {
width: 100%;
height: 50px;
padding: 0;
}
.field-container--focus {
border-color: #86b7fe;
outline: 0;
box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);
}
.field-container--valid {
border-color: #198754;
}
.field-container--valid.field-container--focus {
box-shadow: 0 0 0 0.25rem rgba(25, 135, 84, 0.25);
}
.field-container--invalid {
border-color: #dc3545;
}
.field-container--invalid.field-container--focus {
box-shadow: 0 0 0 0.25rem rgba(220, 53, 69, 0.25);
}
</style>
<script>
//...
const paymentForm = new window.PaymentForm({
//...
containerClassName: 'field-container',
//...
</script>Polices
Dans Web SDK, il est possible d'utiliser des polices système/préinstallées sur les appareils. La police est définie lors de l'initialisation Web SDK dans la propriété styles ou customStyles. Par exemple dans styles.base.fontFamily.
Stylisation des champs de saisie
Vous pouvez personnaliser l'apparence des champs de saisie. Pour cela
- Utilisez l'objet
stylespour les styles de base de tous les champs - Utilisez
customStylespour redéfinir les styles de champs individuels (par exemple, pan, expiry, cvc)
Exemple :
const webSdkPaymentForm = new window.PaymentForm({
// ...
// styles par défaut pour tous les champs de saisie
styles: {
base: {
color: 'black',
padding: '0px 16px',
fontSize: '18px',
fontFamily: 'Arial, sans-serif',
},
// ...
invalid: {
color: 'red',
},
// ...
},
// styles personnalisés pour le champ de saisie du numéro de carte
customStyles: {
pan: {
base: {
color: 'blue',
padding: '0px 24px',
fontSize: '22px',
},
invalid: {
color: 'orange',
},
},
},
});Paramètres supplémentaires
Configurez des paramètres supplémentaires lors de l'initialisation, tels que la langue placeholder, les icônes des systèmes de paiement, le masquage des champs de saisie, etc. La liste complète des paramètres est disponible dans Propriétés d'initialisation PaymentForm.
Validation
WebSDK assure la vérification, la validation et la protection uniquement des champs de saisie principaux requis pour effectuer le paiement : Pan, Expiry, CVC.
Tous les autres champs supplémentaires, tels que Cardholder name, Phone, Email, les champs assurant l'exécution des exigences mandat Visa Secure Data etc., le marchand doit les valider de manière autonome de son côté.
Exemples d'expressions régulières pour la validation de champs supplémentaires :
Cardholder name:
// regex: ^[A-zÁÉÍÑÓÚÜáéíñóúü][A-zÁÉÍÑÓÚÜáéíñóúü'\.\s]* [A-zÁÉÍÑÓÚÜáéíñóúü][A-zÁÉÍÑÓÚÜáéíñóúü'\.\s]*$
export function validateCardholderName({
errorMessage = 'Invalid cardholder'
} = {}) {
return (value) => {
const regex = /^[A-zÁÉÍÑÓÚÜáéíñóúü][A-zÁÉÍÑÓÚÜáéíñóúü'\.\s]* [A-zÁÉÍÑÓÚÜáéíñóúü][A-zÁÉÍÑÓÚÜáéíñóúü'\.\s]*$/;
return regex.test(String(value).trim()) ? null : errorMessage;
};
}Email:
// regex: ^(([^<>()[\].,;:\s@"]+(\.[^<>()[\].,;:\s@"]+)*)|(".+"))@(([^<>()[\].,;:\s@"]+\.)+[^<>()[\].,;:\s@"]{2,})$
export function validateEmail({
errorMessage = 'Invalid email'
} = {}) {
return (value) => {
const regex = /^(([^<>()[\].,;:\s@"]+(\.[^<>()[\].,;:\s@"]+)*)|(".+"))@(([^<>()[\].,;:\s@"]+\.)+[^<>()[\].,;:\s@"]{2,})$/i;
return regex.test(String(value).toLowerCase()) ? null : errorMessage;
};
}Phone:
// regex: ^\+\d{7,15}$
export function validatePhone({
errorMessage = 'Invalid phone number'
} = {}) {
return (value) => {
const regex = /^\+\d{7,15}$/;
return regex.test(String(value).trim()) ? null : errorMessage;
};
}Paiement sans carte sauvegardée (liaison)
Étape 1. Exécuter la méthode d'initialisation
Après avoir défini les paramètres du Web SDK, il est nécessaire d'appeler init().
Cette fonction retourne un callback, où vous pouvez, par exemple, masquer le chargeur ou faire autre chose.
init() retourne c.
Par exemple :
webSdkFormWithoutBindings
.init()
.then((success) => {
console.log('success', success)
// Script initialisé avec succès. Promise retourne un objet contenant des informations utiles sur la commande enregistrée
// dans la passerelle de paiement. Après cela, vous pouvez retirer le loader ou effectuer d'autres actions :
document
.querySelector(".payment-form-loader")
.classList.remove("payment-form-loader--active");
})
.catch((error) => {
// Des erreurs se sont produites lors de l'initialisation du script. L'exécution ultérieure est impossible.
// Promise retourne un message d'erreur que nous pouvons afficher sur la page :
const errorEl = document.querySelector('#error_1');
errorEl.innerHTML = e.message;
errorEl.classList.remove('visually-hidden');
})
.finally(() => {
// Actions exécutées après l'initialisation, indépendamment de son exécution réussie ou non.
});Étape 2. Traitement du clic sur le bouton de paiement. Exécuter la méthode de paiement
Pour effectuer le paiement, appelez la fonction doPayment().
Il n'est pas nécessaire d'envoyer les données de la carte, le Web SDK le fera. doPayment() retourne une Promise.
La méthode accepte les paramètres suivants :
jsonParams: { "t-shirt-color": "noir", "size": "M" }
Mise à jour du mandat Visa Secure Data Field
Veuillez noter les exigences IPS Visa concernant les champs de données supplémentaires nécessaires pour les demandes d'authentification EMV 3DS. Les marchands doivent fournir des données de transaction complètes et précises dans leurs demandes d'authentification. Les marchands doivent également garantir que l'URL de la méthode 3DS effectue la collecte de données d'appareil pour soutenir une authentification réussie dans le cas où l'URL de la méthode 3DS est fournie par l'émetteur.
Ainsi, la collecte de champs supplémentaires pour VISA est de la responsabilité du marchand. Vous pouvez consulter le texte complet des exigences dans Visa Secure Data Field Mandate.
Les champs supplémentaires sont transmis comme propriétés de l'objet, qui est l'argument de la fonction doPayment().
Exemple d'appel :
webSdkFormWithoutBindings
.doPayment({
// Paramètres supplémentaires
email: "foo@bar.com",
phone: "4420123456789",
cardholderName: "JOHN DOE",
jsonParams: { foo: "bar" },
})
.then((result) => {
console.log("result", result);
})
.catch((e) => {
// Traitement des erreurs. Par exemple, affichons le bloc d'erreur
errorEl.innerHTML = e.message;
errorEl.classList.remove("visually-hidden");
})
.finally(() => {
// S'exécute dans tous les cas. Par exemple, rendre le bouton "Payer" à nouveau actif.
payButton.disabled = false;
spinnerEl.classList.add("visually-hidden");
});Démonstration sans carte sauvegardée
Pour des fins de travail - enregistrez la commande via API.
Ce formulaire est utilisé uniquement à des fins de démonstration - utilisez la valeur Order ID =
xxxxx-xxxxx-xxxxx-xxxxxTout le code de démonstration
<div class="container_demo">
<div class="about">
<form name="formRunTest">
<label for="mdOrder"> Order ID (mdOrder) <br>
<span class="label__desc">(Doit provenir du backend. Cette saisie est destinée uniquement à la démonstration)</span>
</label>
<div class="run-test">
<input id="mdOrder" type="text" placeholder="Paste the mdOrder registered for sandbox"/>
<button class="btn-mini" id="load" type="submit">Load</button>
</div>
</form>
</div>
<div class="payment-form">
<div class="payment-form-loader payment-form-loader--active">
Web SDK Payment nécessite la présence de mdOrder (commande préalablement enregistrée dans la passerelle).<br>
Enregistrer une commande est possible via Merchant Portal ou via API. <br><br>
Ou essayez d'utiliser <code>xxxxx-xxxxx-xxxxx-xxxxx</code> si vous voulez obtenir seulement le formulaire de paiement.
</div>
<div class="card-body">
<div class="col-12">
<label for="pan" class="form-label">Numéro de carte</label>
<div id="pan" class="form-control"></div>
</div>
<div class="col-6 col-expiry">
<label for="expiry" class="form-label">Date d'expiration</label>
<div id="expiry" class="form-control"></div>
</div>
<div class="col-6 col-cvc">
<label for="cvc" class="form-label">CVC / CVV</label>
<div id="cvc" class="form-control"></div>
</div>
<!-- Champs supplémentaires pour Visa Mandatory -->
<div class="col-12 additional-field" style="display:none;">
<label for="" class="form-label">Cardholder</label>
<div id="cardholder" class="additional-field-container">
<input type="text" class="additional-field-input" placeholder="Surname"value="JOHN DOE">
</div>
</div>
<div class="col-12 additional-field" style="display:none;">
<label for="" class="form-label">Mobile phone</label>
<div id="mobile" class="additional-field-container">
<input type="tel" class="additional-field-input" placeholder="+4915112345" value="+4915112345678">
</div>
</div>
<div class="col-12 additional-field" style="display:none;">
<label for="" class="form-label">Email address</label>
<div id="email" class="additional-field-container">
<input type="text" class="additional-field-input" placeholder="address@mail" value="address@mail.com">
</div>
</div>
</div>
<button class="btn btn-primary btn-lg" type="submit" id="pay">
<span
class="spinner-border spinner-border-sm me-2 visually-hidden"
role="status"
aria-hidden="true"
id="pay-spinner">
</span>
<span>Payer</span>
</button>
<!-- Ces boutons ne sont nécessaires que pour la démonstration du fonctionnement de la méthode destroy -->
<button class="btn btn-secondary" type="button" id="destroyFormWithoutCredentials">
<span>Détruire</span>
</button>
<button class="btn btn-secondary" type="button" id="reinitFormWithoutCredentials" style="display: none;">
<span>Initialiser</span>
</button>
<div class="error my-2 text-center text-danger visually-hidden" id="error"></div>
</div>
</div>
<script>
document.addEventListener("DOMContentLoaded", () => {
// Fonction pour l'initialisation du formulaire de paiement avec possibilité de réutilisation après destruction
function initPaymentForm() {
const mrOrderInput = document.getElementById("mdOrder");
mrOrderInput.classList.remove("invalid");
if (!/(\w+-){3,10}\w+/g.test(mrOrderInput.value)) {
mrOrderInput.classList.add("invalid");
return;
}
// Initialisation Web SDK. L'identifiant de commande mdOrder est nécessaire.
initPayment(mrOrderInput.value);
}
document.formPageTest.addEventListener("submit", function (e) {
e.preventDefault();
// Initialisation du formulaire de paiement
initPaymentForm();
});
let webSdkFormWithoutBindings;
// Tableau d'objets pour les champs supplémentaires, qui contiennent l'id du champ, son modèle de validation et les caractères autorisés pour la saisie
const mandatoryFieldsWithoutBinding = [
{
id: '#cardholder',
template: /^[a-zA-Z '`.\-]{4,24}$/,
replace: /[^a-zA-Z ' \-`.]/g,
},
{
id: '#mobile',
template: /^\+?[1-9][0-9]{7,14}$/,
replace: /[^0-9\+]/g,
},
{
id: '#email',
template: /^[a-zA-Z0-9._-]{1,64}@([a-zA-Z0-9.-]{2,255})\.[a-zA-Z]{2,255}$/,
replace: /[^a-zA-Z0-9@._-]/g,
}
]
function initPayment(mdOrder) {
webSdkFormWithoutBindings = new window.PaymentForm({
mdOrder: mdOrder,
onFormValidate: () => {},
language: "en", // Langue (anglais)
containerClassName: "field-container",
autoFocus: true,
showPanIcon: true,
panIconStyle: {
height: "16px",
top: "calc(50% - 8px)",
right: "8px",
},
fields: {
pan: {
container: document.querySelector("#pan"),
},
expiry: {
container: document.querySelector("#expiry"),
},
cvc: {
container: document.querySelector("#cvc"),
},
},
styles: {
base: {
padding: "0px 16px",
color: "black",
fontSize: "18px",
fontFamily: 'monospace',
},
invalid: {
color: "red",
},
placeholder: {
base: {
color: "gray",
},
focus: {
color: "transparent",
},
},
},
});
// Action après l'initialisation
webSdkFormWithoutBindings.init().then(() => {
document
.querySelector(".payment-form-loader")
.classList.remove("payment-form-loader--active");
})
.catch((e) => {
// Affichage de l'erreur lors de l'initialisation webSDK
const errorEl = document.querySelector('#error_1');
errorEl.innerHTML = e.message;
errorEl.classList.remove('visually-hidden');
}
.finally(() => {
// Validation et remplacement automatique des caractères non autorisés
mandatoryFieldsWithBinding.forEach(item => {
const field = document.querySelector(item.id)
field.closest(".additional-field").style.display = '';
field.addEventListener('input', () => {
let inputValue = field.querySelector('input')
inputValue.value = item.replace ? inputValue.value.replace(item.replace,'') : inputValue.value;
if (item.id.includes("#cardholder")) {
inputValue.value = inputValue.value.toUpperCase()
}
if (item.template) {
// Classe CSS ".additional-field-invalid" pour afficher les champs non valides
if (item.template.test(inputValue.value)) {
field.classList.remove("additional-field-invalid")
} else {
field.classList.add("additional-field-invalid")
}
}
})
})
})
}
// Gestionnaire "Payer"
document.querySelector("#pay").addEventListener("click", () => {
const payButton = document.querySelector("#pay");
// Désactivons le bouton "Payer" pour éviter les doubles paiements
payButton.disabled = true;
// Affichons le chargeur à l'utilisateur
const spinnerEl = document.querySelector("#pay-spinner");
spinnerEl.classList.remove("visually-hidden");
// Masquons le conteneur d'erreur
const errorEl = document.querySelector("#error");
errorEl.classList.add("visually-hidden");
// Validation des champs supplémentaires Visa Mandatory
if (document.querySelectorAll('.additional-field-invalid').length) {
errorEl.innerHTML = "Form is not valid";
errorEl.classList.remove('visually-hidden');
spinnerEl.classList.add('visually-hidden');
return
}
// Commençons le processus de paiement
webSdkFormWithoutBindings
.doPayment({
// Paramètres supplémentaires
email: document.querySelector('#email input').value,
phone: document.querySelector('#mobile input').value,
cardholderName: document.querySelector('#cardholder input').value,
jsonParams: { size: "L" },
})
.then((result) => {
console.log("result", result);
})
.catch((e) => {
// Exécuté en cas d'erreur
errorEl.innerHTML = e.message;
errorEl.classList.remove("visually-hidden");
})
.finally(() => {
// S'exécute dans tous les cas, par exemple, nous rendons le bouton "Payer" à nouveau actif.
payButton.disabled = false;
spinnerEl.classList.add("visually-hidden");
});
});
// Gestionnaire "Détruire"
document
.querySelector("#destroyFormWithoutCredentials")
.addEventListener("click", function () {
// Supprimons le bouton "Détruire" et affichons le bouton "Initialiser" pour la démonstration
this.style.display = "none";
document.querySelector("#reinitFormWithoutCredentials").style.display =
"";
// Détruisons le formulaire de paiement Web SDK
webSdkFormWithoutBindings.destroy();
});
// Gestionnaire "Initialiser le formulaire"
document
.querySelector("#reinitFormWithoutCredentials")
.addEventListener("click", function () {
// Supprimons le bouton "Initialiser" et affichons le bouton "Détruire" pour la démonstration
this.style.display = "none";
document.querySelector("#destroyFormWithoutCredentials").style.display =
"";
// Initialisation du formulaire de paiement
initPaymentForm();
});
});
</script>Paiement avec une carte sauvegardée (liaison)
Étape 1. Exécuter la méthode d'initialisation
Après avoir défini les paramètres du Web SDK, il est nécessaire d'appeler init().
Cette fonction retourne un callback, où vous pouvez, par exemple, masquer le chargeur ou faire autre chose.
init() retourne une Promise.
Par exemple :
// Initialisation
webSdkFormWithBindings
.init()
.then(({ orderSession }) => {
// L'objet `orderSession` contient toutes les informations sur la commande, y compris les informations sur les identifiants sauvegardés (sur la liaison).
console.info("orderSession", orderSession);
// Afficher l'élément de sélection de la carte sauvegardée
document.querySelector("#select-binding-container").style.display =
orderSession.bindings.length ? "" : "none";
// Remplir la sélection avec les identifiants sauvegardés
orderSession.bindings.forEach((binding) => {
document
.querySelector("#select-binding")
.options.add(new Option(binding.pan, binding.id));
});
// Traitement de la sélection des identifiants sauvegardés ou d'une nouvelle carte
document
.querySelector("#select-binding")
.addEventListener("change", function () {
const bindingId = this.value;
if (bindingId !== "new_card") {
webSdkFormWithBindings.selectBinding(bindingId);
// Masquer la case à cocher "Sauvegarder la carte"
document.querySelector("#save-card-container").style.display = "none";
} else {
// La sélection de liaison avec null signifie le passage à une nouvelle carte
webSdkFormWithBindings.selectBinding(null);
// Afficher la case à cocher "Sauvegarder la carte"
document.querySelector("#save-card-container").style.display = "";
}
});
// Quand le formulaire est prêt, nous pouvons masquer le chargeur
document.querySelector("#pay-form-loader").classList.add("visually-hidden");
})
.catch((error) => {
// Des erreurs se sont produites lors de l'initialisation du script. L'exécution ultérieure est impossible.
// Promise retourne un message d'erreur que nous pouvons afficher sur la page :
const errorEl = document.querySelector('#error_1');
errorEl.innerHTML = e.message;
errorEl.classList.remove('visually-hidden');
});Étape 2. Traitement du clic sur le bouton de paiement. Exécuter la méthode de paiement
Pour effectuer le paiement, appelez la fonction doPayment().
Il n'est pas nécessaire d'envoyer les données de la carte, le Web SDK le fera. doPayment() retourne une Promise.
La méthode accepte les paramètres suivants :
document.querySelector('#save-card').checked
jsonParams: { "t-shirt-color": "noir", "size": "M" }
Application des cartes sauvegardées
Pour payer avec des identifiants sauvegardés, vous devez passer le bindingId sélectionné dans le formulaire avant d'appeler doPayment :
webSdkFormWithBindings.selectBinding('bindingId');
Si vous changez d'avis et voulez payer avec une nouvelle carte, n'oubliez pas de supprimer le bindingId du formulaire :
webSdkFormWithBindings.selectBinding(null);
Exemple d'appel :
webSdkFormWithBindings
.doPayment({
// Paramètres supplémentaires
email: "foo@bar.com",
phone: "4420123456789",
saveCard: document.querySelector("#save-card").checked,
cardholderName: "JOHN DOE",
jsonParams: { foo: "bar" },
})
.then((result) => {
console.log("result", result);
})
.catch((e) => {
// Traitement des erreurs. À titre d'exemple, nous afficherons un bloc avec l'erreur
errorEl.innerHTML = e.message;
errorEl.classList.remove("visually-hidden");
})
.finally(() => {
// Exécuter dans tous les cas. Par exemple, rendre le bouton "Payer" à nouveau actif.
payButton.disabled = false;
spinnerEl.classList.add("visually-hidden");
});Démonstration avec carte sauvegardée
Pour les objectifs de travail - enregistrez la commande via l'API.
Ce formulaire est utilisé uniquement à des fins de démonstration - utilisez la valeur Order ID =
xxxxx-xxxxx-xxxxx-xxxxxTout le code de démonstration
<div class="container_demo">
<div class="about">
<form name="formRunTest">
<label for="mdOrder"> Identifiant de commande (mdOrder) <br/>
<span class="label__desc">(Fourni uniquement à des fins de démonstration. L'identifiant de commande doit provenir du backend.)</span>
</label>
<div class="run-test">
<input id="mdOrder" type="text" placeholder="Insérez le mdOrder enregistré dans Sandbox"/>
<button class="btn-mini" id="load" type="submit">Charger</button>
</div>
</form>
</div>
<div class="payment-form">
<div class="payment-form-loader payment-form-loader--active">
Web SDK Payment nécessite un mdOrder (commande préalablement enregistrée dans la passerelle).<br/>
Vous pouvez enregistrer une commande via l'interface utilisateur ou via l'API. <br/><br/>
Ou essayez d'utiliser <code>xxxxx-xxxxx-xxxxx-xxxxx</code> si vous souhaitez uniquement tester le formulaire de paiement.
</div>
<div id="pay-form-loader" class="spinner-container visually-hidden">
<div class="spinner-border" role="status"></div>
</div>
<div class="card-body">
<div class="col-12" id="select-binding-container" style="display: none">
<select class="form-select" id="select-binding" aria-label="Default select example">
<option selected value="new_card">Paiement avec nouvelle carte</option>
</select>
</div>
<div class="col-12">
<label for="pan" class="form-label">Numéro de carte</label>
<div id="pan" class="form-control"></div>
</div>
<div class="col-6 col-expiry">
<label for="expiry" class="form-label">Date d'expiration</label>
<div id="expiry" class="form-control"></div>
</div>
<div class="col-6 col-cvc">
<label for="cvc" class="form-label">CVC / CVV</label>
<div id="cvc" class="form-control"></div>
</div>
<label class="col-12" id="save-card-container">
<input class="form-check-input" type="checkbox" value="" id="save-card" />
Sauvegarder la carte
</label>
<!--Champs supplémentaires Visa Mandatory -->
<div class="col-12 additional-field" style="display:none;">
<label for="" class="form-label">Titulaire de la carte</label>
<div id="cardholder" class="additional-field-container">
<input type="text" class="additional-field-input" placeholder="NAME SURNAME"value="JOHN DOE">
</div>
</div>
<div class="col-12 additional-field" style="display:none;">
<label for="" class="form-label">Téléphone</label>
<div id="mobile" class="additional-field-container">
<input type="tel" class="additional-field-input" placeholder="+4915112345" value="+4915112345678">
</div>
</div>
<div class="col-12 additional-field" style="display:none;">
<label for="" class="form-label">Courrier électronique</label>
<div id="email" class="additional-field-container">
<input type="text" class="additional-field-input" placeholder="address@mail" value="address@mail.com">
</div>
</div>
</div>
<button class="btn btn-primary btn-lg" type="submit" id="pay">
<span
class="spinner-border spinner-border-sm me-2 visually-hidden"
role="status"
aria-hidden="true"
id="pay-spinner">
</span>
<span>Payer</span>
</button>
<!-- Ces boutons ne sont nécessaires que pour démontrer le fonctionnement de la méthode destroy -->
<button class="btn btn-secondary" type="button" id="destroyFormWithoutCredentials">
<span>Détruire</span>
</button>
<button class="btn btn-secondary" type="button" id="reinitFormWithoutCredentials" style="display: none;">
<span>Initialiser</span>
</button>
<div class="error my-2 visually-hidden" id="error"></div>
</div>
</div>
<script>
document.addEventListener("DOMContentLoaded", () => {
// Fonction pour initialiser le formulaire de paiement pour sa réutilisation après destruction
function initPaymentForm() {
const mrOrderInput = document.getElementById("mdOrder");
mrOrderInput.classList.remove("invalid");
if (!/(\w+-){3,10}\w+/g.test(mrOrderInput.value)) {
mrOrderInput.classList.add("invalid");
return;
}
// Suppression du placeholder
document
.querySelector(".payment-form-loader")
.classList.remove("payment-form-loader--active");
// Ajout du chargeur de formulaires de paiement
document
.querySelector("#pay-form-loader")
.classList.remove("visually-hidden");
// Initialisation Web SDK. Nécessite un mdOrder obligatoire (identifiant de commande).
initPayment(mrOrderInput.value);
}
// Initialisation du gestionnaire pour les données de test
function handleSubmit(e) {
e.preventDefault();
// Initialiser le formulaire de paiement
initPaymentForm();
}
// Enregistrement de l'événement pour l'exemple de saisie
document.formRunTest.addEventListener("submit", handleSubmit);
let webSdkFormWithBindings;
// Tableau d'objets pour les champs supplémentaires, qui contiennent l'id du champ, son modèle de validation et les caractères autorisés pour la saisie
const mandatoryFieldsWithoutBinding = [
{
id: '#cardholder',
template: /^[a-zA-Z '`.\-]{4,24}$/,
replace: /[^a-zA-Z ' \-`.]/g,
},
{
id: '#mobile',
template: /^\+?[1-9][0-9]{7,14}$/,
replace: /[^0-9\+]/g,
},
{
id: '#email',
template: /^[a-zA-Z0-9._-]{1,64}@([a-zA-Z0-9.-]{2,255})\.[a-zA-Z]{2,255}$/,
replace: /[^a-zA-Z0-9@._-]/g,
}
]
function initPayment(mdOrder) {
webSdkFormWithBindings = new window.PaymentForm({
// Numéro de commande (l'enregistrement de la commande se fait avant l'initialisation du formulaire)
mdOrder: mdOrder,
// Traitement de la validation du formulaire
onFormValidate: (isValid) => {
// Par exemple, vous pouvez désactiver les boutons "Payer" et "Obtenir le jeton" si le formulaire n'est pas valide, par exemple :
// const payButton = document.querySelector('#pay');
// payButton.disabled = !isValid;
},
// Contexte pour les appels API
apiContext: "/payment",
// La langue est utilisée pour la localisation des erreurs et des noms pour les placeholders.
// La langue doit être prise en charge dans les paramètres du marchand
language: "en",
// Nom de classe pour les éléments conteneurs contenant l'iframe
containerClassName: "field-container",
// Commutation automatique du focus lors du remplissage des champs
autoFocus: true,
// Afficher l'icône du système de paiement
showPanIcon: true,
// Styles supplémentaires pour l'icône du système de paiement
panIconStyle: {
height: "16px",
top: "calc(50% - 8px)",
right: "8px",
},
// Paramètres de champ
fields: {
// Élément-conteneur dans lequel sera placé l'iframe avec le champ
pan: {
container: document.querySelector("#pan"),
},
// Date d'expiration de la carte
expiry: {
container: document.querySelector("#expiry"),
},
// Code CVC/CVV
cvc: {
container: document.querySelector("#cvc"),
},
},
// Styles supplémentaires pour personnaliser l'apparence des champs de saisie dans les iframes
styles: {
base: {
padding: "0px 16px",
color: "black",
fontSize: "18px",
fontFamily: 'monospace',
},
disabled: {
backgroundColor: "#e9ecef",
},
invalid: {
color: "red",
},
placeholder: {
base: {
color: "gray",
},
focus: {
color: "transparent",
},
},
},
});
// Action après initialisation
webSdkFormWithBindings
.init()
.then(({ orderSession }) => {
// L'objet `orderSession` contient toutes les informations sur la commande, y compris les informations sur les identifiants sauvegardés (sur la liaison).
console.info("orderSession", orderSession);
// Afficher la liaison sélectionnée
document.querySelector("#select-binding-container").style.display =
orderSession.bindings.length ? "" : "none";
// Remplir la sélection avec les identifiants sauvegardés
orderSession.bindings.forEach((binding) => {
document
.querySelector("#select-binding")
.options.add(new Option(binding.pan, binding.id));
});
// Traitement de la sélection des identifiants sauvegardés ou d'une nouvelle carte
document
.querySelector("#select-binding")
.addEventListener("change", function () {
const bindingId = this.value;
if (bindingId !== "new_card") {
// Définir l'identifiant de liaison
webSdkFormWithBindings.selectBinding(bindingId);
// Hide the 'Save card' checkbox
document.querySelector("#save-card-container").style.display =
"none";
} else {
// Sélection de liaison avec null signifie passer à une nouvelle carte
webSdkFormWithBindings.selectBinding(null);
// Afficher la case à cocher "Sauvegarder la carte"
document.querySelector("#save-card-container").style.display = "";
}
});
// Quand le formulaire est prêt, nous pouvons masquer le chargeur
document
.querySelector("#pay-form-loader")
.classList.add("visually-hidden");
// Supprimer l'événement pour l'exemple de saisie
document.formRunTest.removeEventListener("submit", handleSubmit);
})
.catch((error) => {
// Exécuter en cas d'erreur
const errorEl = document.querySelector("#error");
errorEl.innerHTML = e.message;
errorEl.classList.remove("visually-hidden");
})
.finally(() => {
// Validation et remplacement automatique des caractères non autorisés
mandatoryFieldsWithBinding.forEach(item => {
const field = document.querySelector(item.id)
field.closest(".additional-field").style.display = '';
field.addEventListener('input', () => {
let inputValue = field.querySelector('input')
inputValue.value = item.replace ? inputValue.value.replace(item.replace,'') : inputValue.value;
if (item.id.includes("#cardholder")) {
inputValue.value = inputValue.value.toUpperCase()
}
if (item.template) {
// Classe CSS ".additional-field-invalid" pour afficher les champs non valides
if (item.template.test(inputValue.value)) {
field.classList.remove("additional-field-invalid")
} else {
field.classList.add("additional-field-invalid")
}
}
})
})
});
}
// Gestionnaire de paiement
document.querySelector("#pay").addEventListener("click", () => {
// Rendre le bouton "Payer" inactif pour éviter les doubles paiements
const payButton = document.querySelector("#pay");
payButton.disabled = true;
// Afficher le chargeur pour l'utilisateur
const spinnerEl = document.querySelector("#pay-spinner");
spinnerEl.classList.remove("visually-hidden");
// Masquer le conteneur d'erreurs
const errorEl = document.querySelector("#error");
errorEl.classList.add("visually-hidden");
// Validation des champs supplémentaires Visa Mandatory
if (document.querySelectorAll('.additional-field-invalid').length) {
errorEl.innerHTML = "Form is not valid";
errorEl.classList.remove('visually-hidden');
spinnerEl.classList.add('visually-hidden');
return
}
// Commencer le paiement
webSdkFormWithBindings
.doPayment({
// Paramètres supplémentaires
email: document.querySelector('#email input').value,
phone: document.querySelector('#mobile input').value,
cardholderName: document.querySelector('#cardholder input').value,
saveCard: document.querySelector("#save-card").checked,
jsonParams: { foo: "bar" },
})
.then((result) => {
// Ici, on peut faire quelque chose avec le résultat du paiement
console.log("result", result);
})
.catch((e) => {
// Exécuter en cas d'erreur
errorEl.innerHTML = e.message;
errorEl.classList.remove("visually-hidden");
})
.finally(() => {
// Exécuter dans tous les cas. Par exemple, réactiver le bouton « Payer ».
payButton.disabled = false;
spinnerEl.classList.add("visually-hidden");
});
});
// gestionnaire Destroy
document
.querySelector("#destroyFormWithCredentials")
.addEventListener("click", function () {
// Supprimer le bouton Destroy et afficher le bouton de réinitialisation pour la démonstration
this.style.display = "none";
document.querySelector("#reinitFormWithCredentials").style.display = "";
// Exécuter la méthode destroy dans le SDK de formulaire web
webSdkFormWithBindings.destroy();
});
// Déclencher le gestionnaire du formulaire de paiement
document
.querySelector("#reinitFormWithCredentials")
.addEventListener("click", function () {
// Supprimer le bouton Reinit et afficher le bouton Destroy pour la démonstration
this.style.display = "none";
document.querySelector("#destroyFormWithCredentials").style.display = "";
// Initialisation du formulaire de paiement
initPaymentForm();
});
});
</script>Traitement des données renvoyées par les méthodes init( ) et doPayment( )
Méthode init( )
La méthode renvoie une Promise qui, en cas d'exécution réussie, renvoie un objet avec des informations sur la commande enregistrée. Pour des raisons de sécurité, cet objet ne contient pas de données de carte et autres informations confidentielles.
La méthode renvoie les paramètres suivants :
La présence de champs supplémentaires ou l'absence de certains champs de la liste ci-dessus est possible.
Exemple
{
"mdOrder": "5541f44c-d7ec-7a6c-997d-1d4d0007bc7d",
"orderSession": {
"amount": "100000",
"currencyAlphaCode": "BYN",
"currencyNumericCode": "933",
"sessionTimeOverAt": 1740385187287,
"orderNumber": "27000",
"description": "",
"cvcNotRequired": false,
"bindingEnabled": false,
"bindingDeactivationEnabled": false,
"merchantOptions": [
"MASTERCARD_TDS",
"MASTERCARD",
"VISA",
"VISA_TDS",
"CARD"
],
"customerDetails": {},
"merchantInfo": {
"merchantUrl": "http://google.com",
"merchantFullName": "Coffee to Go",
"merchantLogin": "CoffeToGo",
"captchaMode": "NONE",
"loadedResources": {
"logo": true,
"footer": false
},
"custom": false
},
"bindings": [
{
"cardholderName": "CARDHOLDER NAME",
"createdAt": 1712321609666,
"id": "83ffea5d-061f-7eca-912a-02ff0007bc7d",
"pan": "4111 11** **** 1111",
"expiry": "12/24",
"cardInfo": {
"name": "TEST BANK-A",
"nameEn": "TEST BANK-A",
"backgroundColor": "#fbf0ff",
"backgroundGradient": [
"#fafafa",
"#f3f0ff"
],
"supportedInvertTheme": false,
"backgroundLightness": true,
"country": "hu",
"defaultLanguage": "en",
"textColor": "#040e5d",
"url": null,
"logo": "logo/main/293c39ad-0bcb-4cbb-803e-65c435877b5a/1.svg",
"logoInvert": "logo/invert/293c39ad-0bcb-4cbb-803e-65c435877b5a/1.svg",
"logoMini": "logo/mini/293c39ad-0bcb-4cbb-803e-65c435877b5a/1.svg",
"design": null,
"paymentSystem": "visa",
"cobrand": null,
"productCategory": null,
"productCode": null,
"mnemonic": "TEST BANK-A",
"params": null
}
}
]
}
}Exécution non réussie
En cas d'exécution non réussie, Promise renvoie un message d'erreur que nous pouvons afficher sur la page. Exemple de message : Error: Forme invalide.
Des exemples de code de traitement des données renvoyées par la méthode d'initialisation init() sont fournis dans la section Étape 1. Exécuter la méthode d'initialisation pour le paiement sans carte sauvegardée et avec carte sauvegardée.
Méthode doPayment( )
La méthode renvoie Promise, qui lors d'une exécution réussie renvoie un objet avec des informations sur le paiement effectué.
La méthode renvoie les paramètres suivants :
Présence possible de champs supplémentaires, ou absence de certains champs de la liste ci-dessus.
Exemple
{
"redirectUrl": "https://bankhost.com/payment/merchants/ecom/finish.html?orderId=568b2db6-2acc-79e7-9ed8-746a00cd6608&lang=en",
"finishedPaymentInfo": {
"paymentSystem": "MASTERCARD",
"merchantShortName": "CoffeToGo",
"merchantLogin": "CoffeToGo",
"merchantFullName": "Coffee to Go",
"approvalCode": "123456",
"orderNumber": "4003",
"formattedTotalAmount": "15.00",
"backUrl": "https://www.coffeetogo.com/congratulation?orderId=568b2db6-2acc-79e7-9ed8-746a00cd6608&lang=en",
"failUrl": "https://www.coffeetogo.com/someproblem?orderId=568b2db6-2acc-79e7-9ed8-746a00cd6608&lang=en",
"terminalId": "12345678",
"orderDescription": "Order 123",
"displayErrorMessage": "",
"loadedResources": {
"footer": false,
"logo": false
},
"currencyAlphaCode": "EUR",
"orderFeatures": [
"ACS_IN_IFRAME",
"BINDING_NOT_NEEDED"
],
"isWebView": false,
"actionCodeDetailedDescription": "Request processed successfully",
"transDate": "29.11.2024 15:19:30",
"currency": "978",
"actionCode": 0,
"expiry": "12/2024",
"formattedAmount": "15.00",
"actionCodeDescription": "",
"formattedFeeAmount": "0.00",
"email": "address@mail.com",
"amount": "1500",
"merchantCode": "12345678",
"ip": "x.x.x.x",
"panMasked": "555555**5599",
"successUrl": "https://www.coffeetogo.com/congratulation?orderId=568b2db6-2acc-79e7-9ed8-746a00cd6608&lang=en",
"paymentWay": "CARD",
"processingErrorType": {
"value": "NO_ERROR",
"messageCode": "payment.errors.no_error",
"apiErrorCodeMessage": "payment.errors.no_error.code"
},
"panMasked4digits": "**** **** **** 5599",
"amountsInfo": {
"currencyDto": {
"alphabeticCode": "EUR",
"numericCode": "978",
"minorUnit": 2
},
"depositedAmount": {
"value": 1500,
"formattedValue": "15.00"
},
"totalAmount": {
"value": 1500,
"formattedValue": "15.00"
},
"refundedAmount": {
"value": 0,
"formattedValue": "0.00"
},
"approvedAmount": {
"value": 1500,
"formattedValue": "15.00"
},
"feeAmount": {
"value": 0,
"formattedValue": "0.00"
},
"paymentAmount": {
"value": 1500,
"formattedValue": "15.00"
},
"amount": {
"value": 1500,
"formattedValue": "15.00"
},
"depositedTotalAmount": {
"value": 1500,
"formattedValue": "15.00"
}
},
"errorTypeName": "SUCCESS",
"feeAmount": "0",
"totalAmount": "1500",
"orderParams": {
"phone": "+4915112345678",
"foo": "bar",
"paymentMethod": "multiframe-sdk"
},
"orderExpired": false,
"refNum": "111111111111",
"finishPageLogin": "ecom",
"sessionExpired": false,
"cardholderName": "JOHN DOE",
"paymentDate": "29.11.2024 15:19:49",
"merchantUrl": "https://www.coffeetogo.com/",
"status": "DEPOSITED"
}
}Exécution non réussie
En cas d'exécution non réussie, la Promise retourne un message d'erreur que nous pouvons afficher sur la page. Exemple de message : Error: Operation declined. Please check the data and available balance of the account.
Les exemples de code de traitement des données retournées par la méthode doPayment() sont donnés dans la section Étape 2. Traitement du clic sur le bouton de paiement. Exécuter la méthode de paiement pour le paiement sans carte enregistrée et avec carte enregistrée
Redirection automatique après le paiement
Après le paiement, une redirection automatique se produit depuis la page avec Web SDK. Pour traiter l'objet retourné par la méthode doPayment() directement sur la page avec Web SDK, il faut désactiver la redirection automatique après le paiement. Pour cela, une autorisation spéciale est requise dans le système ‒ dans la passerelle de paiement pour ce marchand, la permission Support Acs IFrame activé doit être activée. Pour obtenir l'autorisation, contactez le service de support technique de la banque. Aussi, lors de l'initialisation du Web SDK, l'objet transmis doit contenir la propriété shouldHandleResultManually: true.
Par exemple :
webSdkForm = new window.PaymentForm({
...
shouldHandleResultManually: true,
...
});Web SDK dans React SPA
Lors de l'utilisation du Web SDK dans une single page application (SPA) sur React, il est nécessaire d'initialiser le Web SDK en exécutant la méthode webSdkPaymentForm.init() à chaque rendu initial de la page avec le formulaire Web SDK dans la SPA.
Lors de l'événement de réinitialisation de la page avec le formulaire Web SDK (c'est-à-dire lors de la transition vers une autre page), il est nécessaire d'exécuter la méthode webSdkPaymentForm.destroy(). Ceci est important, car il ne doit rester qu'un seul gestionnaire de formulaire webSDK (multiframe-commutator) sur la page.
Lors du retour sur la page avec le formulaire Web SDK, il est nécessaire de procéder à nouveau à l'initialisation à l'aide de la méthode webSdkPaymentForm.init().
Veuillez noter que l'utilisation de cette bibliothèque nécessite la conformité à la norme PCI DSS, car elle traite les données de carte. Plus d'informations sur PCI DSS ici.
Exemple de composant React
import { useEffect, useRef } from "react";
function addScript(src) {
return new Promise((resolve, reject) => {
const script = document.createElement("script");
script.setAttribute("src", src);
script.addEventListener("load", resolve);
script.addEventListener("error", reject);
document.body.appendChild(script);
});
}
function App() {
const panRef = useRef(null);
const expiryRef = useRef(null);
const cvcRef = useRef(null);
const selectBindingRef = useRef(null);
const saveCardContainerRef = useRef(null);
const payButtonRef = useRef(null);
let webSdkPaymentForm = null;
useEffect(() => {
const initPaymentForm = async () => {
await addScript(
"https://dev.bpcbt.com/payment/modules/multiframe/main.js",
);
webSdkPaymentForm = new window.PaymentForm({
mdOrder: mdOrder,
containerClassName: "field-container",
onFormValidate: (isValid) => {
// Gestion de la validation du formulaire
},
apiContext: "/payment",
language: "en",
autoFocus: true,
showPanIcon: true,
panIconStyle: {
height: "16px",
top: "calc(50% - 8px)",
right: "8px",
},
fields: {
pan: {
container: panRef.current,
onFocus: (containerElement) => {
// Gestion du focus
},
onBlur: (containerElement) => {
// Gestion de la perte de focus
},
onValidate: (isValid, containerElement) => {
// Gestion de la validation
},
},
expiry: {
container: expiryRef.current,
// Configuration du champ date d'expiration
},
cvc: {
container: cvcRef.current,
// Configuration du champ CVC/CVV
},
},
styles: {
base: {
padding: "0px 16px",
color: "black",
fontSize: "18px",
fontFamily: 'monospace',
},
focus: {
color: "blue",
},
disabled: {
color: "gray",
},
valid: {
color: "green",
},
invalid: {
color: "red",
},
placeholder: {
base: {
color: "gray",
},
focus: {
color: "transparent",
},
},
},
});
webSdkPaymentForm
.init()
.then(({ orderSession }) => {
console.info("orderSession", orderSession);
if (orderSession.bindings.length) {
selectBindingRef.current.style.display = "";
} else {
selectBindingRef.current.style.display = "none";
}
if (orderSession.bindingEnabled) {
saveCardContainerRef.current.style.display = "";
} else {
saveCardContainerRef.current.style.display = "none";
}
orderSession.bindings.forEach((binding) => {
const option = new Option(binding.pan, binding.id);
selectBindingRef.current.options.add(option);
});
})
.catch(() => {
// Gestion de l'erreur d'initialisation
});
};
initPaymentForm();
return () => {
if (webSdkPaymentForm) {
webSdkPaymentForm.destroy();
}
};
}, []);
const handlePayment = () => {
payButtonRef.current.disabled = true;
webSdkPaymentForm
.doPayment({})
.then((result) => {
// Gestion du paiement réussi
})
.catch((e) => {
alert("Error");
})
.finally(() => {
payButtonRef.current.disabled = false;
});
};
const handleSelectBinding = () => {
const bindingId = selectBindingRef.current.value;
if (bindingId !== "new_card") {
webSdkPaymentForm.selectBinding(bindingId);
saveCardContainerRef.current.style.display = "none";
} else {
webSdkPaymentForm.selectBinding(null);
saveCardContainerRef.current.style.display = "";
}
};
return (
<div className="container">
<div className="websdk-form">
<div className="card-body">
<div
className="col-12"
id="select-binding-container"
onChange={handleSelectBinding}
>
<select
className="form-select"
id="select-binding"
ref={selectBindingRef}
aria-label="Default select example"
>
<option value="new_card">Payer avec une nouvelle carte</option>
</select>
</div>
<div className="col-12 input-form">
<label htmlFor="pan" className="form-label">
Numéro de carte
</label>
<div id="pan" className="form-control" ref={panRef}></div>
</div>
<div className="col-6 col-expiry input-form">
<label htmlFor="expiry" className="form-label">
Expiration
</label>
<div id="expiry" className="form-control" ref={expiryRef}></div>
</div>
<div className="col-6 col-cvc">
<label htmlFor="cvc" className="form-label">
CVC / CVV
</label>
<div id="cvc" className="form-control" ref={cvcRef}></div>
</div>
<label className="col-12" id="save-card-container">
<input
className="form-check-input me-1"
ref={saveCardContainerRef}
type="checkbox"
value=""
id="save-card"
/>
Sauvegarder la carte
</label>
</div>
<div className="pay-control">
<button
className="btn btn-primary btn-lg"
type="submit"
id="pay"
ref={payButtonRef}
onClick={handlePayment}
>
Payer
</button>
</div>
<div
className="error my-2 text-center text-danger visually-hidden"
id="error"
></div>
</div>
</div>
);
}
export default App;