...
Figure 3 : Cinématique d’un paiement avec 3DSecure déclenché par le module LCLF
...
Cinématique d’enregistrement d’une carte dans un
...
portefeuille
...
Dans ce scénario, aucun paiement n’est réalisé.
...
- Le navigateur de l’acheteur envoie les données de la carte à Payline (sur le module des pages Web de paiement) ;
- Payline retourne un token chiffré si les données sont conformes (cf. la première cinématique) ;
- Le navigateur retourne ces données au serveur commerçant ;
- Le serveur commerçant :
- déchiffre les données pour récupérer : le token, la date d’expiration et le CVV virtuel ;
- appelle le WS Payline « createWallet ».
- Le WS Payline « createWallet » :
- appelle le tokenizer pour récupérer le numéro de carte réel et retransforme le CVV virtuel en CVV réel ;
- envoie une demande d’autorisation pour scoring à la banque du commerçant (ex : autorisation à 1 euro ou demande d'information selon la banque).
4.5. Cinématique d’un second paiement par token
...
Figure 4 : Cinématique d’un enregistrement de carte dans le portefeuille Payline
Cinématique d’un second paiement par token
Dans cette cinématique, le commerçant a conservé au préalable le token de la carte (et la date d’expiration) dans sa base de données lors du premier paiement. La page de paiement affiche les cartes associées à ce compte acheteur.
Le commerçant a la possibilité de collecter le CVV auprès de son acheteur et le fournir lors de l’appel « doAuthorization » ou d’effectuer une transaction sans CVV.
Lorsque l’acheteur valide la commande :
- Le serveur commerçant
- recherche le token carte associé au client ;
- appelle WS Payline « doAuthorization » avec le token carte, avec ou sans CVV et un code action 120 ou 121.
- Le WS Payline « doAuthorization »
- appelle le tokenizer pour récupérer le numéro de carte réel ;
- envoie une autorisation à la banque du commerçant.
...
...
Implémentation
...
Modification de la gestion des clés commerçant
Les commerçants qui vont utiliser les paiements en mode AJAX doivent générer une clé d’accès à Payline avec le nouveau module de gestion. Ce nouveau module permet d’attribuer une référence de clé à chaque clé générée. Il est accessible au travers du centre d’administration dans le menu « Configuration ».
...
La clé elle-même garde les mêmes usages qu’auparavant.
Figure 6 : nouvel écran de génération de clé d’accès
...
Fonction de création de token
5.2.1. Description
Description
Ce Ce service est développé sous la forme d’une Servlet nommée « getToken ». Il est à appeler au niveau de la page du commerçant pour obtenir le token de la carte de l’acheteur (requête AJAX ou requête POST+redirection).
...
Données en entrée
Nom du champ | Obligatoire | Format | Commentaire |
Données à générer par le commerçant | |||
accessKeyRef | O | AN(20) | Référence de la clé d’accès du commerçant |
data | O | AN | Données commerçant : Clé secrète : SHA-256 de la clé d’accès commerçant |
returnURL | F | AN | URL de retour sur le site du commerçant. |
Données de la carte saisies par l’acheteur | |||
cardNumber | O | N(19) | Numéro de la carte |
cardExpirationDate | F | MMYY | Date d’expiration |
cardCvx | F | N(4) | CVV |
...
Le champ data contient les informations suivantes (valeurs séparées par des points virgules) :
Index | Valeur | Obligatoire | Format |
1 | Identifiant du commerçant | O | AN(19) |
2 | Référence unique de commande générée par le commerçant | O | N(50) |
3 | Numéro de contrat | O | AN(50) |
...
Remarque : Le nombre de requêtes getToken est limité à 3 tentatives pour chaque référence commande. Si vous obtenez 3 erreurs lors d’une commande, il faudra re-générer une chaine chiffrée qui se base sur une nouvelle référence de commande.
...
Données en sortie
Le service retourne soit une liste de valeur encodées, soit un code erreur si la fonction ne s’est pas déroulée correctement.
Nom du champ | Obligatoire | Format | Commentaire |
Données à générer par le commerçant | |||
data | F | AN | Retourné si la fonction se déroule correctement. Données : Clé secrète : la même qu'en entrée |
errorCode | F | N(5) | Fourni en cas d’erreur, cf. tableau suivant |
...
Le champ data contient les informations suivantes (valeurs séparées par des points virgules) :
Index | Valeur | Obligatoire | Format |
1 | Token associé au numéro de de carte Ex : 497910AztyqdEGdn123 | O | AN(19) |
2 | Date d’expiration de la carte (même donnée qu’en entrée) | F | MMYY |
3 | CVV virtuel, il devra être restitué dans la demande d’autorisation sans être modifié Ex. : v456 | F | AN(5) |
4 | Référence commande identique à celle passée en entrée. Pour éviter un éventuel rejeu, le commerçant doit contrôler que cette référence est bien celle attendue. Si tel n’est pas le cas, il doit refuser la transaction et envoyer une annulation à Payline | O | AN(50) |
5 | Type de carte Ex. : VISA | O | AN |
6 | Indicateur « isCVD » (carte virtuelle) | O | Y ou N |
7 | Code pays de la carte Ex. : FR | F | AN(2) |
8 | Code produit de la carte Ex. : L (pour Electron) | F | AN(3) |
9 | Code de la banque émettrice de la carte Ex : 30003 | F | AN(11) |
...
Codes d’erreur
Code | Message court | Message long |
09101 | Transaction refused | Accès non autorisé |
09102 | Transaction refused | Compte commerçant bloqué ou désactivé |
02703 | Transaction refused | Action non autorisée |
02303 | Transaction refused | Numéro de contrat invalide |
02623 | Transaction refused | Nombre d’essai maximal atteint |
02624 | Transaction refused | Carte expirée |
02625 | Transaction refused | Format du numéro de carte incorrect |
02626 | Transaction refused | Format de la date d’expiration incorrect ou date non fournie |
02627 | Transaction refused | Format du CVV incorrect ou CVV non fourni |
02628 | Transaction refused | Format de l’URL de retour incorrect |
02631 | Transaction refused | Délai d’attente expiré |
...
API webservice
Les webservices Payline ont évolué de façon à être compatible avec le paiement en mode Ajax.
...
Production : https://secure.payline.com/webpayment/getToken
...
Exemples d’implémentation
...
Préparation de la page de collecte des données carte
Avant de présenter la page à ses acheteurs, le commerçant doit préparer les éléments qui serviront à envoyer la demande de token à Payline :
...
À noter que la chaîne de caractères chiffrée (obtenue à l’étape 5) doit être encodée en base64url (cf. https://fr.wikipedia.org/wiki/Base64#base64url).
...
Code serveur PHP
Préparer un hash de la clé d’accès :
Bloc de code | ||||
---|---|---|---|---|
| ||||
$aes256Key = hash("SHA256", $accessKey, true); |
La donnée accessKey représente la clé d’accès du commerçant
...
Chiffrer les données. Il faut d’abord concaténer les données avant de chiffrer la chaine de caractères obtenue :
Bloc de code | ||||
---|---|---|---|---|
| ||||
messageUtf8 = utf8_encode($merchantId.";".$orderRef.";".$contractNumber); |
...
$crypted crypted = getEncrypt($messageUtf8, $aes256Key); |
...
function getEncrypt($message, $key){ |
...
$block = mcrypt_get_block_size('rijndael_128', 'ecb'); |
...
$pad = $block - (strlen($message) % $block); |
...
$message .= str_repeat(chr($pad), $pad); |
...
return base64_url_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $message, MCRYPT_MODE_ECB)); |
...
} |
Avec :
merchantId : identifiant Payline du commerçant
orderRef : référence unique de la commande en cours
contractNumber : numéro de contrat Payline sur lequel va porter le paiement.
...
Code serveur J2E
Préparer un hash de la clé d’accès :
Bloc de code | ||||
---|---|---|---|---|
| ||||
MessageDigest sha = MessageDigest.getInstance("SHA-256"); |
...
aes256Key = sha.digest(accessKey.getBytes("UTF-8")); |
La donnée accessKey représente la clé d’accès du commerçant.
...
Chiffrer les données. Il faut d’abord concaténer les données avant de chiffrer la chaîne de caractères obtenue :
Bloc de code | ||||
---|---|---|---|---|
| ||||
byte[] msgUtf8 = (merchantId+orderRef+ContractNumber).getBytes("UTF-8"); |
SecretKeySpec secretKeySpec = new SecretKeySpec(accessKeyBytes, "AES"); |
Cipher cipher = Cipher.getInstance("AES"); |
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec); |
byte[] ciphered = cipher.doFinal(msgUtf8); |
String crypted = Base64.encodeBase64URLSafeString(ciphered); |
Avec :
merchantId : identifiant Payline du commerçant ;
orderRef : référence unique de la commande en cours ;
contractNumber : numéro de contrat Payline sur lequel va porter le paiement.
...
Page de paiement
La page de paiement doit implémenter un fonctionnement qui garantit que le numéro de carte saisit par l’acheteur ne sera jamais stocké (ni par le navigateur de l’acheteur, ni par le serveur web du commerçant).
...
En retour un traitement côté serveur web commerçant doit être appelé afin de prendre en compte la réponse et retourner la page adéquate à l’acheteur (ticket/confirmation de paiement ou page d’erreur selon les cas).
Exemple de script d’appel AJAX :
Bloc de code | ||||
---|---|---|---|---|
| ||||
<script src="http://code.jquery.com/jquery-1.10.1.min.js"></ |
...
<script>
...
script> <script> // Requête AJAX pour appeler la fonction getToken de Payline |
...
$(document).ready( function () |
...
{ $("#paymentForm").submit( function() { |
...
// à la soumission du |
...
formulaire jQuery.support.cors = true; |
...
// activer les requêtes ajax cross-domain |
...
$.ajax( |
...
{ type: "POST", |
...
url: "https://homologation-webpayment.payline.com/webpayment/getToken", |
...
data: "data="+$("#data").val() + "&accessKeyRef=" + $("#accessKeyRef").val() + "&cardNumber=" + $("#cardNumber").val() + "&cardExpirationDate=" + $("#cardExpirationDate").val() + "&cardCvx=" + $("#cardCvx").val(), |
...
success: function(msg){ // si l'appel a bien |
...
fonctionné $.ajax({ |
...
// fonction permettant de faire de l'ajax |
...
type: "POST", // methode de transmission au site |
...
marchand url: "paymentAjax.php", |
...
// traitement serveur (appel local) |
...
data: "resultPayline=" |
...
success: function(result){ |
...
// si l'appel a bien |
...
fonctionné // traitement du résultat OK (afficher les parametres dans cet |
...
var divMsg = $(result);
...
exemple) var divMsg = $(result); divMsg.hide(); |
...
$("#result").append(divMsg); |
...
divMsg.slideDown(); |
...
}
});
},
error:function (xhr, status, error){
...
} }); }, error:function (xhr, status, error){ console.log("Erreur lors de l'appel de Payline : " + xhr.responseText + " (" + status + " - " + error + ") |
...
}
});
return false; // pour rester sur la même page à la soumission du formulaire
});
});
</script>
");
}
});
return false; // pour rester sur la même page à la soumission du formulaire
});
});
</script> |
Exemple utilisable dans un formulaire du type :
Bloc de code | ||||
---|---|---|---|---|
| ||||
<form id="paymentForm" action="#" method="post"> |
<input type="hidden" name="data" id="data" size='255' value="<?php echo $crypted ?>" /> |
<input type="hidden" name="accessKeyRef" id="accessKeyRef" value="<?php echo $accessKeyRef ?>" /> |
<label for="">Numéro de carte</label> |
<input type="text" name="cardNumber" id="cardNumber"/> |
<label for="">Date d'expiration</label> |
<input type="text" name="cardExpirationDate" id="cardExpirationDate"/> |
<label for="">Cryptogramme</label> |
<input type="text" name="cardCvx" id="cardCvx"/> |
<br/> |
<input type="submit" class="btn btn-primary" value="Payer" /> |
</form> |
Avec :
crypted : données chiffrées préparées à l’étape précédente
accessKeyRef : référence de la clé d’accès commerçant
...
...
Traitement de la réponse
La réponse contient des données qu’il faut déchiffrer et décompresser, le tout étant codé en base64url.
...
Pour plus d’information sur l’API webservice, vous pouvez consulter la documentation associée.
...
Serveur PHP
Déchiffrer les données reçues :
Bloc de code | ||||
---|---|---|---|---|
| ||||
$zippedData = getDecrypt($data, $aes256Key); |
...
function getDecrypt($message, $key){ |
...
$message = base64_url_decode($message); |
...
$message = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $message, MCRYPT_MODE_ECB |
...
); $block = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB); |
...
$len = strlen($message); |
...
$pad = ord($message[$len-1]); |
...
return substr($message, 0, $len-$pad); } |
...
La donnée data représente la valeur du paramètre ($_POST['data']) reçu en retour (cas où l’appel ne retourne pas une erreur).
Décompresser les données :
Bloc de code | ||||
---|---|---|---|---|
| ||||
$uncompressedData = gzdecode($zippedData); |
Découper le résultat pour récupérer le résultat de l’appel :
Bloc de code | ||||
---|---|---|---|---|
| ||||
$paylineDataResponse=explode(';', $uncompressedData); |
...
$cardToken = $paylineDataResponse[0]; |
...
$cardExpirationDate = $paylineDataResponse[1]; |
...
$cardVirtualCVV = $paylineDataResponse[2]; |
...
$orderReference = $paylineDataResponse[3]; |
...
… |
...
Serveur J2E
Déchiffrer les données reçues :
Bloc de code | ||||
---|---|---|---|---|
| ||||
byte[] decryptedMessage = new byte[0]; |
...
zippedData = AESEncryption.decrypt(aes256Key, data); |
...
public static final byte[] decrypt(final String key, final String message) { |
...
byte[] decrypt = new byte[0]; |
...
MessageDigest sha = MessageDigest.getInstance("SHA-256"); |
...
keyBytes = sha.digest(key.getBytes("UTF-8")); |
...
SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, "AES"); |
...
Cipher cipher = Cipher.getInstance("AES"); |
...
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec); |
...
decrypt = cipher.doFinal(Base64.decodeBase64(message.getBytes("UTF-8"))); |
...
return finalDecrypt; } |
...
La donnée data représente la valeur du paramètre (request.getParameter("data")) reçu en retour (cas où l’appel ne retourne pas une erreur).
...
Décompresser les données :
Bloc de code | ||||
---|---|---|---|---|
| ||||
final StringBuffer outStr = new StringBuffer(); |
...
final ByteArrayInputStream gzipedStr = new ByteArrayInputStream(zippedData); |
...
final GZIPInputStream gis = new GZIPInputStream(gzipedStr); |
...
final BufferedReader bf = new BufferedReader(new InputStreamReader(gis)); |
...
String line; |
...
while ((line = bf.readLine()) != null) { |
...
outStr.append(line); |
...
} |
...
gis.close(); |
...
String uncompressedData = outStr.toString(); |
Découper le résultat pour récupérer le résultat de l’appel :
Bloc de code | ||||
---|---|---|---|---|
| ||||
String[] paylineDataResponse = uncompressedData.split(";"); |
...
String cardToken = paylineDataResponse[0]; |
...
String cardExpirationDate = paylineDataResponse[1]; |
...
String cardVirtualCVV = paylineDataResponse[2]; |
...
String orderReference = paylineDataResponse[3]; |
...
… |