Technologie – Conseils et astuces pour créer, gérer et financer votre association https://www.helloasso.com/blog Vous êtes président d’une association, membre du bureau ou bénévole ? Retrouvez sur notre blog tous les conseils et astuces pour créer, gérer et financer votre association. Articles de loi, procédures, démarches administratives, conseils, outils pratiques et retours d’expérience, ici les associations parlent aux associations ! Suivez les actualités HelloAsso, retrouvez nos conseils pour gagner du temps et de l’efficacité dans l’organisation et la gestion de vos projets associatifs. Inscrivez-vous à notre Newsletter pour ne rien manquer des dernières informations et profitez de l’expérience et des conseils de nos équipes qui agissent au quotidien pour faciliter votre activité associative Tue, 25 Jun 2024 09:01:58 +0000 fr-FR hourly 1 https://www.helloasso.com/blog/wp-content/uploads/2018/10/favicon-helloasso-paiement-pour-association-150x150.png Technologie – Conseils et astuces pour créer, gérer et financer votre association https://www.helloasso.com/blog 32 32 De 1 million à 1 milliard : Les principes clés pour gérer la croissance sereinement — partie 3 https://www.helloasso.com/blog/de-1-million-a-1-milliard-les-principes-cles-pour-gerer-la-croissance-sereinement-partie-3/ Tue, 25 Jun 2024 09:01:10 +0000 https://www.helloasso.com/blog/?p=47538 Nous voici arrivés au terme de cette épopée, enfin au début du terme !

Si vous n’avez pas lu le début de notre aventure technique, je vous conseille de commencer par lire le premier et le deuxième article avant celui-ci.

Comme je le disais dans l’article précédent : Il est temps de rembourser notre dette technique.
Notre histoire continue donc avec une équipe technique solide et talentueuse prête à affronter des défis à sa hauteur.

La Conquête du Sommet

Notre team de super héros prête à tout pour gravir les sommets

À cette époque on récupère une solution HelloAsso vieillissante avec un objectif clair et simple :

  • réduire notre dette technique

On commence par yoyoter pendant quelques semaines sur la question

Est-ce qu’on restart from scratch ?

On finit par aboutir sur un non.
5 ans plus tard, je dirai que c’était une mauvaise décision car la migration n’est pas 100% terminée et nous avons hérité de beaucoup de lourdeur du passé. Mais aurions-nous été meilleurs from scratch ? Je n’en suis pas sûr non plus !
Ce qui est important c’est que nous ayons fait ce choix par consensus.

On doit donc réfléchir à un plan pour améliorer notre solution.
On commence donc par s’enfermer pendant 3 jours pour mettre en place ce que l’on appellera HelloAsso.Core, il s’agit de notre framework interne gérant l’accès à la donnée (DAL) et ce que l’on peut qualifier de couche service.
On organise tout ça selon le pattern Command avec les fameuses CommandRequest et CommandResponse et on se pose des contraintes de qualités très importantes, grâce au test unitaire notamment. On  pense également à anticiper les évolutions futures :

  • .Net Core en développant tout ça en .Net Standard
  • L’utilisation d’une couche front et déployant une nouvelle API et en arrêtant l’utilisation de ASP.NET MVC

On est satisfait du résultat et en termes de retour d’expérience cette architecture ne sera pas remise en question, ça valait donc largement l’investissement.

Voilà pour la partie code, reste maintenant à mettre en place une structure solide pour délivrer de la feature.
On s’oriente tout naturellement vers la création de feature team en se basant sur le modèle proposé par spotify et ses fameuses squads.
Ce framework est pratique car il permet la subdivision de façon native et n’est donc pas une vision statique de l’organisation ce qui est assez pratique dans une start-up en hyper croissance. Je vous dévoile tout de suite l’ensemble de l’évolution jusqu’à aujourd’hui.

On voit très clairement les phases de développement, avec la multiplication des squads puis de rationalisation des périmètres avec des suppressions ou changement d’intitulé. Pour aboutir en 2023 sur une organisation par domaine, un domaine regroupant au minimum 2 squads. 2024 hérite des méthodes shape-up & safe mettant au centre des domaines des équipes “enablers” dont le but est d’aider les équipes à effectuer leurs missions dans les meilleures conditions.
Je ne vous cache pas qu’une telle organisation, même si elle est dans le modèle de ce qui se fait partout, prend beaucoup de temps à mettre en œuvre et réside surtout dans le juste dimensionnement et la bonne affectation des personnes et des périmètres.

Je vais prendre l’exemple de l’équipe Admin Sys -> DevOps -> Infra & Cloud mais c’est vrai pour l’ensemble des équipes étant maintenant enablers.
Lors de la mise en place des squads nous avons fait les bons élèves et mis l’ensemble des compétences dans les squads. Celà a eu un effet bénéfique sur l’alignement au début mais un autre très néfaste sur la stabilité de notre infra. On a donc pris la décision de mettre en place une équipe DevOps dédiée dans le but de se concentrer sur notre infra et de la stabiliser. 2 mois après les résultats étaient là. Aujourd’hui nous avons renommé cette équipe en Infra & Cloud pour porter un message fort : le DevOps est l’affaire de tous !
L’équipe est donc bien toujours à part et se focalise plus sur l’infra et à une rôle de formation de l’ensemble de l’équipe tech aux pratiques DevOps.

La Transformation

Avec une base de code et une organisation solide on ne pouvait que se mettre en position d’accélérer.

Comme les plus attentifs d’entre vous l’auront vu dès 2021 nous commençons à travailler sur notre établissement de paiement (EP). Seulement avec les personnes en place c’est très compliqué d’être partout.
Fin 2022 on boucle donc une nouvelle levée de fonds 15M et comme on le voit sur notre organisation c’est à partir de ce moment là que l’on met en place une stratégie par domaine car on a maintenant une équipe tech & produit de 60 personnes.

L’organisation et l’architecture sont donc à la hauteur de nos ambitions ce qui nous permet d’adresser des projets qui avant n’étaient tout simplement pas possible à adresser convenablement.
Je pense notamment à la mise en place d’une logique constante de réduction des coûts de notre infra et plus récemment à la mesure de notre bilan carbone et l’élaboration d’un schéma directeur du numérique responsable.

La Légende

Et maintenant de quoi sera fait l’avenir ?

Comme on l’a vu notre organisation est à l’épreuve du scale et ne devrait pas évoluer de façon très importante. L’architecture mise en place est fiable et permet d’intégrer de l’innovation facilement. Ce qui va changer principalement c’est la version des technologies utilisées et que l’on fait suivre en fonction de l’actualité.

Au niveau de l’infrastructure il y a aussi quelques transformations et migrations à terminer mais l’ensemble est là encore très stable. L’idée étant surtout de respecter ce que l’on a mis dans notre schéma directeur.
Si on parle de l’avenir on va donc plutôt parler de produits. Les deux principaux étant notre établissement de paiement et notre API.

Notre établissement de paiement est un choix stratégique depuis 2021 pour offrir à nos associations encore plus de facilité dans la réception de leur paiement.

Notre API est aujourd’hui utilisé dans beaucoup de cas d’utilisation mais on pense pouvoir aller beaucoup plus loin et offrir encore plus de fonctionnalités aux intégrateurs de nos solutions.

J’espère que cette suite d’articles vous a plu. N’hésitez pas à régir.

]]>
De 1 million à 1 milliard : Les principes clés pour gérer la croissance sereinement — partie 2 https://www.helloasso.com/blog/de-1-million-a-1-milliard-les-principes-cles-pour-gerer-la-croissance-sereinement-partie-2/ Fri, 07 Jun 2024 09:48:50 +0000 https://www.helloasso.com/blog/?p=47535 Cet article est la suite de ma série d’articles sur l’épopée technique de HelloAsso.

Si vous n’avez pas lu le premier c’est peut-être bien de commencer par ça !

Comme je le spoile à la fin du premier article, le développement du B2B à eu plusieurs effets négatifs, le principal étant l’impossibilité de rester focus sur notre produit principal. Voyons plus en détail ce qu’il s’est passé.

Premiers Pas et Premiers Obstacles

Ce chapitre pourrait aussi être intitulé la traversée du désert de la tech 🏜️

Revenons sur notre outil d’appel à projets.
Celui-ci rencontre un franc succès et notre commercial s’en donne à cœur joie.
Je vous rappelle que de façon synthétique il s’agit d’un seul script js de 5000 lignes. Un seul et unique script et en face 10 clients avec des spécificités.

is_required() the delicious way

Je vous laisse imaginer les régressions à chaque mise en prod.

Au final on corrige vite est-ce donc si grave ?
Au final c’est du temporaire est-ce donc si grave ?
C’est assez découplé de la solution principale, est-ce donc si grave ?

Rétrospectivement je dirais que ce n’était pas si grave et la période était totalement excitante. On faisait des choses sympas avec des technos hype, l’équipe était hyper soudée même si on travaillait tous beaucoup trop !

C’est d’autant moins grave, car nous étions conscients de l’état de notre tech et c’est l’époque où notre CTO s’est mis à parler d’un sujet qui lui tenait à cœur : la dette technique.
Car oui à cette époque le site HelloAsso est mis de côté et on fait seulement passer des correctifs prioritaires qu’il faut traiter dans un temps record.

Et attention je ne parle pas de petit correctif d’orthographe.

Quand tu organises un appel à projets avec campagne de vote, que les emails sont envoyés de façon synchrone par ton site et que le jour de la finale tu te retrouves à envoyer 100 000 mails en quelques heures, tu apprends très rapidement l’intérêt d’une architecture orientée service et tu livres en prod le service de mail le soir même.

Si on résume, on a donc toujours :

  • le site HelloAsso (avec une belle surcouche js)
  • un service qui gère les paiements récurrents
  • un service email qui dépile une file d’attente
  • un service qui gère les versements mensuels (exécuté sur le poste d’un dev)

C’est bon on a cassé le monolithe, plus de refonte avant 10 ans !

Arrive ensuite l’époque où l’on intègre l’ère (de l’AdminSys) du DevOps en recrutant notre premier AdminSys.
Il a fallu beaucoup de batailles pour justifier d’un tel poste, surtout avec un parc informatique de dix personnes, un seul serveur et des pipelines CI/CD à leur balbutiement.
C’est d’ailleurs souvent le cas pour les nouveaux postes. Il est toujours difficile d’arriver à estimer que l’on a besoin d’un temps plein. Si je peux vous donner un conseil c’est intéressant de commencer par de la prestation pour se donner une meilleure idée et surtout des chiffres pour justifier. C’est d’ailleurs la méthode que j’ai employée quand il a fallu recruter un DBA.
Bref revenons à notre sujet, l’équipe technique c’est maintenant 2 devs .net, un AdminSys et le CTO !

Cette période marque un tournant historique, car c’est à ce moment-là qu’on prend le virage du cloud, on est en 2015.

Et encore une fois c’est un virage que l’on prend un peu dans la douleur !
L’élément déclencheur ? Juste le crash du disque dur de notre serveur qui était up depuis 5 ans !
On galère pas mal avec le support pour au final obtenir notre serveur fonctionnel, mais avec un disque tout neuf et bien sûr à l’époque, la configuration du backup était dans la colonne “do it tomorrow”.

Concrètement on a tout perdu, game over

Alors on fait quoi ?
On se dit qu’on est probablement les plus chanceux !

  • Notre admin sys venait de faire un backup de la base et des contenus utilisateurs 30 minutes avant.
  • On était en cours de POC pour migrer sur le cloud

C’est donc là qu’intervient ce fameux virage dans la douleur.

Ce que j’en retiens:

  • Un environnement cloud n’est pas un serveur, revoir l’ensemble de ta gestion de fichier utilisateur pour passer d’un système de fichier à un storage en une journée est un très beau défi (surtout quand cette gestion n’est pas centralisée).
  • De même pour la migration d’un service Windows en service cloud.
  • Les estimations c’est useless, on a fait en moins de 48h ce que l’on avait chiffré à 3 mois.

La leçon la plus importante d’entre toutes et qui s’est vérifiée lors de chaque migration : quand tu prévois une migration quelconque, il y a de fortes chances que tu te retrouves à la faire dans l’urgence, n’attends donc pas le dernier moment pour la tester.

Toujours est-il que nous voilà maintenant sur le cloud !

une infra cloud des plus modestes

Bon maintenant on travaille sur HelloAsso ou on s’éparpille encore ?

La Croisée des Chemins

Ne me demandez pas comment, mais on saisit deux opportunités énormes pour l’époque.

Tout d’abord en 2016 un partenariat avec Orange consistant à développer une plateforme de collecte en Côte d’Ivoire.
Pour le coup on développe une solution from scratch, le métier étant très différent de ce que l’on fait.
C’est une expérience hyper enrichissante qui nous a permis de découvrir qu’on pouvait faire des paiements en low tech, l’ensemble des transactions étant basé sur USSD.

En 2017 on développe une marque blanche pour la banque postale : rue des associations.
Le projet étant globalement la modification de l’ensemble du style de notre site, le recrutement d’un intégrateur web designer n’était encore une fois pas une bêtise.

Notre codebase commence à devenir assez importante d’autant que nous avons eu la bonne idée de faire un fork pour la marque blanche !
L’exploitation des différents sites devient donc aussi très chronophage.

Bien évidemment nous n’allions pas rester indéfiniment dans cet état végétatif d’un côté et hyperactif de l’autre.

Pour se refocaliser sur HelloAsso, on réalise une levée de fond de 6 millions d’euros qui nous permet de muscler les équipes dans le but de reprendre le contrôle de notre solution.

Nous sommes en 2018, l’équipe technique c’est : 2 dev / 1 admin sys et 1 intégrateur / web designer et le CTO

On se muscle côté support tech, on lance le développement de notre application mobile et surtout on intègre deux devs back C# senior.
L’objectif est clair :

Il est temps de commencer à rembourser notre dette technique

La suite de cette série arrive très prochainement. Ce sera l’article le plus dense, car je parlerais de l’architecture choisie et qui tient depuis 4 ans, de la façon dont on a résolu nos problématiques de fiabilité côté infra et les enjeux auxquels nous sommes confrontés actuellement.

]]>
De 1 million à 1 milliard : Les principes clés pour gérer la croissance sereinement — partie 1 https://www.helloasso.com/blog/de-1-million-a-1-milliard-les-principes-cles-pour-gerer-la-croissance-sereinement-partie-1/ Fri, 07 Jun 2024 09:48:26 +0000 https://www.helloasso.com/blog/?p=47522 Cet article est le premier d’une série de trois articles retraçant l’épopée technique de HelloAsso. Il mettra en lumière l’évolution de l’infrastructure, du produit et de l’équipe.

J’ai eu la chance de rejoindre HelloAsso en 2013 quand il y avait encore tout à faire alors j’ai de quoi raconter !

Embarquons ensemble dans cette fantastique aventure en commençant par l’état actuel des choses.

Le 25/03/2023 à 11:08:04 HelloAsso atteignait le seuil du milliard d’euros collecté pour les associations, après 14 ans d’existence !

Au-delà des chiffres et des retombés de presse que cela a pu avoir, j’ai envie de vous raconter l’histoire de l’intérieur. Étant dans l’équipe technique, il y a fort à parier que mon histoire est un biais technique et plus particulièrement DevOps.

HelloAsso est la plateforme qui permet aux citoyens de participer aux activités d’une association près de chez eux. Nos services sont offerts aux responsables associatifs grâce aux contributions volontaires des citoyens. A l’aide de ces soutiens et de nos outils, les associations peuvent gérer leurs activités et collecter des paiements en ligne dont elles perçoivent 100% de chaque somme, sans frais ni commission.

Ce produit connaît une énorme croissance et nous avons de beaux chiffres (de juin 2023) si on veut en mettre plein les yeux:

  • 150+ collaborateurs·rices
  • 222 000+ associations
  • 11 000 000+ d’utilisateurs
  • 1 065 000 000+ euros collectés
  • 20 000 000+ de paiement en base

On prend le temps de regarder les courbes, personnellement j’adore les tendances exponentielles !

Évolution du nombre de paiement avec le temps

Évolution du nombre d’association avec le temps

Évolution de la collecte avec le temps

Évolution de l’effectif avec le temps

Le tout supporté par une infrastructure au début très modeste et maintenant totalement sur le cloud, en réalisant l’exploit de ne pas avoir suivi les autres tendances.
Autant pour la croissance l’exponentielle c’est sympa, mais pour les dépenses on préfère une bonne fonction affine.

Évolution du coût de l’infra avec le temps

Maintenant que l’on sait ça, on peut voir que l’on est en pleine croissance. Cela étant dit, on n’en sait pas beaucoup plus sur la façon dont on en est arrivé là !
Je vous propose donc un voyage dans le temps pour comprendre nos difficultés et nos succès.

L’Étincelle Initiale

Nous sommes en 2013, après avoir testé beaucoup de choses, l’équipe initiale lance officiellement le site HelloAsso. Dans sa première version, il s’agit d’un site de collecte en ligne.

C’est un site très classique et dans les technos de l’époque :

  • ASP.Net
  • jQuery / vanilla js
  • vanilla css
  • SQL Serveur

Cette application est organisée avec le pattern trois tiers à notre sauce et c’est un beau monolith.

Côté infrastructure, le tout déployé sur un unique serveur Windows Server chez OVH.

Le pipeline CI/CD de l’époque ?
Click droit / Deploy to sur visual studio et surtout on croise les doigts pour ne pas perdre la connexion lors du déploiement !
Ça c’était avant de faire des zip que l’on décompresse sur le serveur.

Le monitoring et la maintenance du serveur de l’époque ?
Si ça fonctionne pourquoi se poser des questions

Ce site évolue tranquillement, les features s’ajoutent au fur et à mesure notamment les billetteries et les adhésions.

Le Rassemblement des Héros

Nous avons un site fonctionnel, une volumétrie correcte mais un CA ne permettant pas de payer les salaires. C’est d’ailleurs tout l’enjeu d’un business model basé sur la générosité : avec une masse critique ça fonctionne, sans ça et bien il faut attendre la croissance.

Alors en parallèle on lance une activité B2B. L’idée est vraiment très bonne, on a entre 20 000 et 50 000 associations en base de données, en face il y a des fondations d’entreprise qui lancent des appels à projet mais ne parviennent pas à obtenir beaucoup de participation.

On développe donc une solution d’appel à projet totalement configurable pour nous permettre de déployer un appel à projet en un temps record.
Bien évidemment ça c’est la théorie.

C’est une solution full js (chose assez novatrice à l’époque). Vuejs sortait en même temps dans sa première version, c’est-à-dire !

Techniquement c’est géré par deux tables en base de données, une API pour gérer l’interaction avec le front qui n’est rien d’autre qu’une page html remplie entièrement par du js.

Tu penses que la suite est un long fleuve tranquille ?


Qu’opérer un site et une activité B2B se passe sans aucune surprise ?
L’article suivant arrive très bientôt, il abordera les difficultés avant les périodes plus joyeuses.

]]>
Mes premiers pas avec l’API HelloAsso https://www.helloasso.com/blog/mes-premiers-pas-avec-lapi-helloasso/ Fri, 07 Jun 2024 09:47:54 +0000 https://www.helloasso.com/blog/?p=47505

Comme beaucoup de sites, HelloAsso dispose d’une API. Cet article repasse très rapidement sur les bases : c’est quoi une API, à quoi ça sert, comment on l’utilise ?
Puis nous nous attarderons sur les bases de l’utilisation de l’API HelloAsso.
Après avoir lu cet article vous devriez être en mesure de :

Maitriser l’authentification à l’API HelloAsso et à tout autre API utilisant OAuth2;
Être autonome dans la lecture de la documentation de l’API HelloAsso;
Récupérer vos données depuis l’API HelloAsso.

C’est quoi une API ?

Une API (application programming interface), en français interface de programmation d’application, est un élément d’un système informatique qui permet de le connecter à un autre système informatique.

C’est bon vous êtes autonome ?

Une fois cette base posée il convient de passer par quelques exemples et comparatifs avec la vie courante pour se représenter concrètement ce qu’est une API.

La comparaison que l’on trouve le plus souvent sur internet est celle du restaurant; le postulat étant que le serveur du restaurant n’est ni plus ni moins qu’une API. C’est lui qui se charge de faire la traduction entre deux systèmes à savoir le client et la cuisine.

Maintenant je pense que c’est plus clair, mais est-ce qu’une API c’est vraiment utile et utilisé ?

C’est un grand OUI !

Des API il y en a partout

par exemple les applications qui recensent l’ensemble les moyens de transport public pour se rendre d’un point A à un point B utilise l’ensemble des API misent place par les différentes administrations ou compagnies pour vous afficher les trajets les plus rapides, les moins coûteux …
Rien que pour Bordeaux on compte presque 100 API liées au déplacement.

Utilisation

De façon très simple, utiliser une API reviens à faire une simple requête HTTP.

Il est donc tout à fait possible d’effectuer un appel API depuis son navigateur. Pour mes exemples j’utilise l’api affirmations.dev qui retourne une phrase aidant les développeurs à l’affirmation de soi !

Ici je fais un appel GET à une API

En revanche et pour plus de praticité, il est généralement recommandé d’utiliser un outil dédié et spécialisé. Le plus basique d’entre eux est probablement l’utilitaire en ligne de commande curl, mais il existe aussi des outils comme Postman qui offrent un nombre important de fonctionnalités.

Et là, le même appel en utilisant curl.

Sécurité

Une API permet donc de récupérer des données d’un site tiers, mais aussi dans certain cas d’en ajouter, modifier ou supprimer.

La sécurité est donc une préoccupation prise en compte par les équipes en charge de la création d’API, et doit également être considérée par les personnes cherchant à les utiliser.

Certaines API sont publiques comme celle de mes exemples https://www.affirmations.dev/. D’autres nécessitent une authentification comme l’API HelloAsso https://api.helloasso.com/v5/swagger/ui/index#/

Méthode d’authentification

Il existe plusieurs méthodes d’authentification pour accéder à une API. On en distingue deux principales : basic et oauth2.

Ces méthodes d’authentification demandent de modifier l’entête de la requête.

Basic

Cette méthode d’authentification consiste à passer un utilisateur et un mot de passe dans l’entête de la requête. La spécification impose que l’utilisateur et le mot de passe soient séparés par le caractère “:” et encodé en base 64.

Voici un exemple pour l’utilisateur eddy et le mot de passe password.

Première étape on ajoute le caractère “:” : eddy:password
On encode le tout en base 64 : ZWRkeTpwYXNzd29yZA==
On ajoute enfin cette chaine de caractère au header de notre requête

J’utilise curl avec l’option — header pour ajouter un entête

Oauth2

Oauth2 est la norme en vigueur à date de rédaction de cet article. Il s’agit d’une authentification plus complexe, mais permettant une meilleure sécurité et une flexibilité plus importante dans la gestion des droits. La spécification est un peu dense, mais elle peut se résumer à un schéma simple 😉

Vu comme ça, il est légitime de se demander quel est l’intérêt de cette méthode qui ajoute des appels API pour un même résultat ?

Deux aspects sont à considérer : la sécurité et la granularité des accès.

Avec la méthode basic, lors de chaque requête, le client transmet l’utilisateur et le mot de passe. Dans la pratique, il arrive que les clients en viennent à stocker ces informations de manière non sécurisées, pour ne pas avoir à les redemander à l’utilisateur lors de chaque requête. En Oauth2, l’utilisateur fournit ses identifiants uniquement lors du premier appel et le client stocke seulement un token.

Du côté de la granularité, en basic le serveur revérifie lors de chaque appel si l’utilisateur a bien le droit d’accéder à la ressource qu’il demande; ce qui n’est pas le cas avec Oauth2, car le token envoyé par le serveur d’authentification contient les droits d’accès. La seule vérification consiste à vérifier que le token est authentique.

À noter qu’il existe deux types de token : access_token et refresh_token. Ces deux tokens sont émis par le serveur d’authentification et ont un rôle bien précis. Le token d’accès doit être envoyé au serveur de données pour chaque requête et, comme son nom l’indique, c’est lui qui permet l’accès aux données. Sa durée de vie est courte (de l’ordre de quelques minutes ou heures), il est donc nécessaire d’en obtenir un nouveau fréquemment. C’est là qu’intervient le token de rafraichissement : il dispose d’une durée de vie bien plus longue (semaine ou mois) et peut donc être utilisé sur le serveur d’authentification pour obtenir un nouveau token d’accès.

Vous l’aurez bien compris, Oauth2 c’est mieux !

L’API HelloAsso

Maintenant que l’on a vu les principes généraux, il est temps de nous attaquer à l’API HelloAsso et à son utilisation.

Avant de se lancer, je ne peux que trop vous conseiller d’installer postman, qui vous servira pour comprendre les retours de l’API et générer automatiquement du code pour votre langage favori.
Il est aussi important de prendre le temps de parcourir la documentation de l’API ou, au minimum, de l’avoir à proximité !

Comme on vient de le voir juste au-dessus, la première étape va consister à récupérer un token et à le rafraichir. C’est donc à partir de maintenant que l’on rentre dans le vif du sujet.

Authentification

L’url de l’API d’authentification est la suivante : https://api.helloasso.com/oauth2/token.

Pour obtenir nos tokens il va donc falloir jouer avec les paramètres que l’on peut passer. Il en existe quatre : client_idclient_secretrefresh_tokengrant_type.
Le client_id et client_secret sont les informations de connexion.
Le refresh_token est un token retourné par l’API qu’il sera nécessaire de passer uniquement quand on cherchera à récupérer un nouveau token d’accès.
Le grant_type spécifie l’action que l’on souhaite effectuer avec l’API, il peut prendre plusieurs valeurs (implicit, authorization_code, client_credentials, password, refresh_token), chez HelloAsso nous utilisons seulement client_credentials pour la récupération initiale et refresh_token pour rafraichir notre token d’accès.

Le client id et secret sont à récupérer dans le backoffice HelloAsso dans le menu Intégrations et API.

Récupération de clé dans sur helloasso.com

Récupération initiale

curl --request POST \
--url https://api.helloasso.com/oauth2/token \
--header ‘Content-Type: application/x-www-form-urlencoded’ \
--data client_id=mon-id \
--data client_secret=mon-secret \
--data grant_type=client_credentials

Il y a deux choses importantes ici. – Le content-type : il s’agit d’un type d’entête qui spécifie au serveur la méthode avec laquelle on lui transmet les paramètres, pour l’API d’authentification il s’agit de application/x-www-form-urlencoded. – Le paramètre grant_type dont la valeur doit être à client_credentials pour l’obtention du token initial, elle sera valorisée à refresh_token pour rafraichir le token; jusqu’ici tout est logique. Si l’appel se passe bien il retourne un objet au format JSON.

{
    "access_token": "mon-access",
    "token_type": "bearer",
    "expires_in": 1800,
    "refresh_token": "mon-refresh"
}

Comme vu précédemment, le token d’accès a une durée de vie courte; ici le temps est de 1800 secondes donc 30 minutes. Si on ne prévoit pas tout de suite le rafraichissement, on a donc une autonomie de 30 minutes devant nous, ce qui est bien court quand on est en phase exploratoire ou de développement. Le token de rafraichissement a lui une durée de vie d’un mois.

Rafraichissement du token

curl --request POST \
--url https://api.helloasso.com/oauth2/token \
--header ‘Content-Type: application/x-www-form-urlencoded’ \
--data client_id=mon-id \
--data refresh_token=mon-refresh \
--data grant_type=refresh_token

Si vous avez bien suivi, cet appel nécessite le token de rafraichissement obtenu précédemment et cette fois-ci le paramètre grant_type prend la valeur refresh_token car nous sommes en train de rafraichir notre token !

Si tout se passe bien, on obtient à nouveau un objet JSON contenant un nouveau token d’accès (valable 30 minutes) et un nouveau token de rafraichissement (valable à nouveau 1 mois). Il est donc possible de rester connecté pour toujours si on effectue au moins un appel par mois à l’API

{
    "access_token": "mon-nouveau-access",
    "token_type": "bearer",
    "expires_in": 1799,
    "refresh_token": "mon-nouveau-refresh"
}

Là encore on peut se poser la question de l’intérêt de rafraichir le token ? Si la réponse est la même et que la durée de vie identique, pourquoi s’embêter à faire un appel différent ?
Il existe plusieurs raisons, mais les plus évidentes sont les suivantes :
– on ne transfère pas ses identifiants toutes les 30 minutes;
– on ne stocke pas les identifiants directement dans le client;
– le serveur d’authentification travaille moins : l’appel initial étant plus lourd qu’un rafraichissement;
– on respecte la norme !

Pagination

Bon aller, une dernière étape : avant de faire notre première requête on va s’intéresser au format de retour de l’API. Cela nous permettra de comprendre plus simplement à quoi on a affaire.

Voici donc un retour API classique, comme pour l’appel d’authentification, c’est un objet JSON :

{
    "data": [
        {}
    ],
    "pagination": {
        "pageSize": 20,
        "totalCount": 1,
        "pageIndex": 1,
        "totalPages": 1,
        "continuationToken": "1234567890"
    }
}

On retrouve deux propriétés : data et pagination.

Data

C’est un tableau qui contient les données intéressantes, telles que liste de formulaires, paiement, commandes, etc. Il est spécifique à chaque point d’API et nécessite donc d’aller voir la documentation pour savoir à quoi s’attendre.

Pagination

Cette partie est là pour nous aider à parcourir l’ensemble des résultats. Comme beaucoup d’API, l’API HelloAsso pagine ses résultats pour la simple et bonne raison qu’il n’est pas performant et utilisable de retourner un grand nombre de résultats. Imaginons un peu la taille d’un JSON contenant 10 000 paiements !
Par défaut la pagination est à 20 résultats par page. Il est possible de l’augmenter en utilisant un paramètre d’url pageSize qui autorise des valeurs de 1 à 100 au-delà l’API retournera une erreur.

{
    "errors": [
        {
            "code": "ArgumentInvalid",
            "message": "La taille de la page demandée doit être comprise entre 1 et 100"
        }
    ]
}

Dans tous les cas on se rend vite compte qu’il va falloir prendre en compte cette pagination dans nos appels API, sauf si on est sûr à 100% de ne jamais avoir plus de 100 éléments ce qui est assez improbable !
Il existe deux méthodes pour gérer la pagination : spécifier la page (malin !) mais aussi utiliser un token de continuation. Les deux méthodes fonctionnent et sont couramment utilisées par les API. Pour la première c’est assez simple il suffit d’agir avec le paramètre pageIndex. Ainsi un appel classique évoluera comme cela :

https://api.helloasso.com/v5/organizations/association-exemple/forms

➡️ Pour augmenter la taille des pages à 100 éléments :

https://api.helloasso.com/v5/organizations/association-exemple/forms?pageSize=100

➡️ Pour récupérer la deuxième page :

https://api.helloasso.com/v5/organizations/association-exemple/forms?pageSize=100&pageIndex=2

Pour la deuxième méthode, cela revient à peu près au même sauf qu’on ne travaille pas avec les pages, mais un token qu’il faut indiquer dans un paramètre nommé continuationToken. Un appel classique va également évoluer :

https://api.helloasso.com/v5/organizations/association-exemple/forms

Pour récupérer la suite, je récupère la valeur du paramètre continuationToken dans le JSON du résultat.

https://api.helloasso.com/v5/organizations/association-exemple/forms?continuationToken=azertyuiop

On se rend donc compte qu’en utilisant la méthode des pages on peut se permettre de sauter une page, tandis qu’avec la méthode des token de continuation, ce n’est pas le cas : il faut nécessairement commencer par un appel initial.

Maintenant que l’on sait obtenir un token, le rafraichir et gérer la pagination, il est temps d’interagir avec ce qui nous intéresse : nos données.

Récupération des formulaires de mon association

Imaginons que je veuille maintenant intégrer la liste de mes formulaires sur le site de mon association. Je vais donc utiliser l’API pour récupérer l’ensemble de mes formulaires.

Pour obtenir le chemin qui m’intéresse, je consulte la documentation de l’API. Il est intéressant de savoir comment sont formés les urls. Le prefix est toujours le même https://api.helloasso.com/v5/ il est ensuite suivi du type de l’entité et de son identifiant (qui peut être un nombre ou une chaine de caractère selon l’entité)

Par exemple pour rechercher des associations:
– prefix : https://api.helloasso.com/v5/
– entité : organizations
– identifiant : association-exemple

On parle ici de paramètre d’url (path), il existe d’autres types de paramètres que l’on vient juste d’utiliser. Pour la pagination par exemple on utilise des paramètres de requête(query) comme ?pageSize= et pour l’authentification des paramètres de corps de requête(body). Comme leur nom l’indique, ces paramètres ne sont pas passés dans la requête, mais directement dans le corps de notre requête.

Autre notion à prendre en compte : la méthode de requête. Il s’agit de ce que l’on peut voir devant les chemins sur la documentation, c’est un paramètre qui peut prendre plusieurs valeurs les principales étant GET et POST, l’API HelloAsso utilise également PUT. Chaque verbe à une utilité propre
– GET permet de faire de la récupération de données
– POST ajoute une entité
– PUT met à jour une entité

Je vais donc utiliser la méthode GET pour récupérer mes formulaires

🤔 C’est exactement ce qu’il me faut !
curl --request GET \
--url https://api.helloasso.com/v5/organizations/association-exemple/forms \
--header ‘Authorization: Bearer mon-access’

Ce qu’il est important de noter ici, c’est le changement de l’url. Ce qui est logique, car on ne fait plus d’authentification :

https://api.helloasso.com/oauth2/token ➡️ https://api.helloasso.com

ENFIN ! On utilise notre token d’accès et donc on le spécifie avec une entête spécifique de type bearer token. Rien de particulier à connaitre, ça ressemble à de l’authentification basic, mais ça n’en est pas !

Encore une fois, si tout se passe bien, on obtient un objet JSON contenant l’ensemble des formulaires de l’association; je peux récupérer les attributs intéressants pour afficher ça sur mon site (banner.publicUrl, title et url). De la sorte, je peux afficher mes formulaires avec un titre, une image et un lien.

{
    "data": [
        {
            "banner": {
                "fileName": "banner.jpg",
                "publicUrl": "https://cdn.helloasso.com/img/photos/evenements/banner.jpg"
            },
            "currency": "EUR",
            "description": "Description de mon événement",
            "startDate": "2020-01-05T17:00:00+01:00",
            "endDate": "2020-01-05T22:30:00+01:00",
            "meta": {
                "createdAt": "2019-09-12T16:35:30+02:00",
                "updatedAt": "2022-07-27T15:02:34.412015+02:00"
            },
            "state": "Public",
            "title": "Nom de mon événement",
            "widgetButtonUrl": "https://www.helloasso.com/associations/association-exemple/evenements/formulaire-exemple/widget-bouton",
            "widgetFullUrl": "https://www.helloasso.com/associations/association-exemple/evenements/formulaire-exemple/widget",
            "widgetVignetteHorizontalUrl": "https://www.helloasso.com/associations/association-exemple/evenements/formulaire-exemple/widget-vignette-horizontale",
            "widgetVignetteVerticalUrl": "https://www.helloasso.com/associations/association-exemple/evenements/formulaire-exemple/widget-vignette",
            "formSlug": "formulaire-exemple",
            "formType": "Event",
            "url": "https://www.helloasso.com/associations/association-exemple/evenements/formulaire-exemple",
            "organizationSlug": "association-exemple"
        },
        {
            "banner": {
                "fileName": "banner.jpg",
                "publicUrl": "https://cdn.helloasso.com/img/photos/adhesions/banner.jpg"
            },
            "currency": "EUR",
            "description": "Description de mon formulaire d'adhésion",
            "startDate": "2018-12-31T23:00:00+01:00",
            "endDate": "2020-06-30T00:00:00+02:00",
            "meta": {
                "createdAt": "2019-09-12T16:35:30+02:00",
                "updatedAt": "2022-07-27T15:02:34.412015+02:00"
            },
            "state": "Public",
            "title": "Nom de mon formulaire d'adhésion",
            "widgetButtonUrl": "https://www.helloasso.com/associations/association-exemple/adhesions/formulaire-exemple/widget-bouton",
            "widgetFullUrl": "https://www.helloasso.com/associations/association-exemple/adhesions/formulaire-exemple/widget",
            "widgetVignetteHorizontalUrl": "https://www.helloasso.com/associations/association-exemple/adhesions/formulaire-exemple/widget-vignette-horizontale",
            "widgetVignetteVerticalUrl": "https://www.helloasso.com/associations/association-exemple/adhesions/formulaire-exemple/widget-vignette",
            "formSlug": "formulaire-exemple",
            "formType": "Membership",
            "url": "https://www.helloasso.com/associations/association-exemple/adhesions/formulaire-exemple",
            "organizationSlug": "association-exemple"
        }
    ],
    "pagination": {
        "pageSize": 20,
        "totalCount": 1,
        "pageIndex": 1,
        "totalPages": 1,
        "continuationToken": "201801221052087"
    }
}

Récupération de la liste des paiements

Imaginons maintenant que j’ai envie d’intégrer les paiements effectués sur HelloAsso dans mon système d’information. Par exemple, si je veux créer un espace personnel à mes payeurs.

La encore, je me rends dans la documentation et je cherche le point d’API qui me permet de faire cela.

Là encore on est pas mal

La requête est donc d’une simplicité déconcertante (après avoir traversé toutes ces aventures) :

curl --request GET \
--url https://api.helloasso.com/v5/organizations/association-exemple/payments \
--header ‘Authorization: Bearer mon-access’

La réponse l’est tout autant mais elle est un peu volumineuse et il peut être difficile de s’y retrouver. Pour me faciliter la tâche, la documentation fournit un exemple de réponse ainsi que la structure de celle-ci

Exemple de réponse
Modèle de réponse

Ce qui m’intéresse pour mon cas d’usage, ce sont les attributs payer et stateIl est très important de faire attention à l’état des paiements. Tous les paiements retournés par l’API ne sont pas nécessairement validés. La documentation décrit l’ensemble des états disponible.

{
    "data": [
        {
            "order": {
                "id": 12578,
                "date": "2019-12-15T17:27:02+01:00",
                "formSlug": "formulaire-exemple",
                "formType": "Event",
                "organizationName": "name",
                "organizationSlug": "association-exemple",
                "formName": "name",
                "meta": {
                    "createdAt": "2019-12-15T17:27:02+01:00",
                    "updatedAt": "2019-12-15T17:27:02+01:00"
                },
                "isAnonymous": false,
                "isAmountHidden": false
            },
            "payer": {
                "email": "john.doe@test.com",
                "address": "23 rue du palmier",
                "city": "Paris",
                "zipCode": "75000",
                "country": "FRA",
                "company": "Hello Asso",
                "firstName": "John",
                "lastName": "Doe"
            },
            "items": [
                {
                    "shareAmount": 11000,
                    "shareItemAmount": 10000,
                    "shareOptionsAmount": 1000,
                    "id": 12578,
                    "amount": 30000,
                    "type": "Membership",
                    "state": "Processed"
                },
                {
                    "shareAmount": 1000,
                    "shareItemAmount": 1000,
                    "id": 12579,
                    "amount": 1000,
                    "type": "Donation",
                    "state": "Processed"
                }
            ],
            "cashOutDate": "2020-02-15T17:27:02+01:00",
            "cashOutState": "CashedOut",
            "paymentReceiptUrl": "https://www.helloasso.com/associations/association-exemple/evenements/formulaire-exemple/paiement-attestation/12578",
            "fiscalReceiptUrl": "https://www.helloasso.com/get-fisc.aspx?dau=159875",
            "id": 159875,
            "amount": 11000,
            "date": "2019-12-15T17:27:02+01:00",
            "paymentMeans": "Card",
            "state": "Authorized",
            "meta": {
                "createdAt": "2019-12-15T17:27:02+01:00",
                "updatedAt": "2019-12-15T17:27:02+01:00"
            }
        },
        {
            "order": {
                "id": 12578,
                "date": "2019-12-15T17:27:02+01:00",
                "formSlug": "formulaire-exemple",
                "formType": "Event",
                "organizationName": "name",
                "organizationSlug": "association-exemple",
                "formName": "name",
                "meta": {
                    "createdAt": "2019-12-15T17:27:02+01:00",
                    "updatedAt": "2019-12-15T17:27:02+01:00"
                },
                "isAnonymous": false,
                "isAmountHidden": false
            },
            "payer": {
                "email": "john.doe@test.com",
                "address": "23 rue du palmier",
                "city": "Paris",
                "zipCode": "75000",
                "country": "FRA",
                "company": "Hello Asso",
                "firstName": "John",
                "lastName": "Doe"
            },
            "items": [
                {
                    "shareAmount": 11000,
                    "shareItemAmount": 10000,
                    "shareOptionsAmount": 1000,
                    "id": 12578,
                    "amount": 30000,
                    "type": "Membership",
                    "state": "Processed"
                },
                {
                    "shareAmount": 1000,
                    "shareItemAmount": 1000,
                    "id": 12579,
                    "amount": 1000,
                    "type": "Donation",
                    "state": "Processed"
                }
            ],
            "cashOutState": "Transfered",
            "paymentReceiptUrl": "https://www.helloasso.com/associations/association-exemple/evenements/formulaire-exemple/paiement-attestation/12578",
            "fiscalReceiptUrl": "https://www.helloasso.com/get-fisc.aspx?dau=159876",
            "id": 159876,
            "amount": 9000,
            "date": "2020-01-15T17:27:02+01:00",
            "paymentMeans": "Card",
            "state": "Authorized",
            "meta": {
                "createdAt": "2020-01-15T17:27:02+01:00",
                "updatedAt": "2020-01-15T17:27:02+01:00"
            }
        },
        {
            "order": {
                "id": 12578,
                "date": "2019-12-15T17:27:02+01:00",
                "formSlug": "formulaire-exemple",
                "formType": "Event",
                "organizationName": "name",
                "organizationSlug": "association-exemple",
                "formName": "name",
                "meta": {
                    "createdAt": "2019-12-15T17:27:02+01:00",
                    "updatedAt": "2019-12-15T17:27:02+01:00"
                },
                "isAnonymous": false,
                "isAmountHidden": false
            },
            "payer": {
                "email": "john.doe@test.com",
                "address": "23 rue du palmier",
                "city": "Paris",
                "zipCode": "75000",
                "country": "FRA",
                "company": "Hello Asso",
                "firstName": "John",
                "lastName": "Doe"
            },
            "items": [
                {
                    "shareAmount": 11000,
                    "shareItemAmount": 10000,
                    "shareOptionsAmount": 1000,
                    "id": 12578,
                    "amount": 30000,
                    "type": "Membership",
                    "state": "Processed"
                },
                {
                    "shareAmount": 1000,
                    "shareItemAmount": 1000,
                    "id": 12579,
                    "amount": 1000,
                    "type": "Donation",
                    "state": "Processed"
                }
            ],
            "id": 159877,
            "amount": 9000,
            "date": "2020-02-15T17:27:02+01:00",
            "paymentMeans": "Card",
            "state": "Pending",
            "meta": {
                "createdAt": "2020-02-15T17:27:02+01:00",
                "updatedAt": "2020-02-15T17:27:02+01:00"
            }
        }
    ],
    "pagination": {
        "pageSize": 20,
        "totalCount": 1,
        "pageIndex": 1,
        "totalPages": 1,
        "continuationToken": "201801221052087"
    }
}

Récupération de la liste objets vendus

Vous prendrez bien un peu de dessert ? Un dernier exemple pour récupérer la liste des commandes : imaginons que j’organise un évènement avec des achats supplémentaires. Il serait sympa de pouvoir sortir directement le récapitulatif des achats que je dois faire (maillot et taille dans le cadre de l’organisation d’une course par exemple). Je pars donc explorer la documentation de nouveau.

On commence à connaitre la chanson

J’en déduis la nouvelle requête, là encore rien de bien compliqué :

curl --request GET \
--url https://api.helloasso.com/v5/organizations/association-exemple/items \
--header ‘Authorization: Bearer mon-access’

Et si tout se passe bien on devrait avoir une réponse de la sorte. Ce qui va nous intéresser là-dedans, c’est l’attribut options. On y retrouvera l’ensemble des options vendues.

{
    "data": [
        {
            "order": {
                "id": 12578,
                "date": "2019-12-15T17:27:02+01:00",
                "formSlug": "formulaire-exemple",
                "formType": "Event",
                "organizationName": "name",
                "organizationSlug": "association-exemple",
                "formName": "name",
                "meta": {
                    "createdAt": "2019-12-15T17:27:02+01:00",
                    "updatedAt": "2019-12-15T17:27:02+01:00"
                },
                "isAnonymous": false,
                "isAmountHidden": false
            },
            "payer": {
                "email": "john.doe@test.com",
                "address": "23 rue du palmier",
                "city": "Paris",
                "zipCode": "75000",
                "country": "FRA",
                "company": "Hello Asso",
                "firstName": "John",
                "lastName": "Doe"
            },
            "payments": [
                {
                    "cashOutState": "CashedOut",
                    "shareAmount": 10000,
                    "id": 159875,
                    "amount": 11000,
                    "date": "2019-12-15T17:27:02+01:00",
                    "paymentMeans": "Card",
                    "state": "Authorized",
                    "meta": {
                        "createdAt": "2019-12-15T17:27:02+01:00",
                        "updatedAt": "2019-12-15T17:27:02+01:00"
                    }
                },
                {
                    "cashOutState": "Transfered",
                    "shareAmount": 30000,
                    "id": 159876,
                    "amount": 9000,
                    "date": "2020-01-15T17:27:02+01:00",
                    "paymentMeans": "Card",
                    "state": "Authorized",
                    "meta": {
                        "createdAt": "2020-01-15T17:27:02+01:00",
                        "updatedAt": "2020-01-15T17:27:02+01:00"
                    }
                },
                {
                    "shareAmount": 30000,
                    "id": 159877,
                    "amount": 9000,
                    "date": "2020-02-15T17:27:02+01:00",
                    "paymentMeans": "Card",
                    "state": "Pending",
                    "meta": {
                        "createdAt": "2020-02-15T17:27:02+01:00",
                        "updatedAt": "2020-02-15T17:27:02+01:00"
                    }
                }
            ],
            "name": "Adhesion Football",
            "user": {
                "firstName": "John",
                "lastName": "Doe"
            },
            "priceCategory": "Fixed",
            "discount": {
                "code": "DISC30 : -30€",
                "amount": 3000
            },
            "customFields": [
                {
                    "name": "BirthDate",
                    "type": "Date",
                    "answer": "1978-09-15"
                },
                {
                    "name": "ZipCode",
                    "type": "Zipcode",
                    "answer": "33600"
                }
            ],
            "options": [
                {
                    "name": "T-Shirt",
                    "amount": 700,
                    "priceCategory": "Fixed",
                    "isRequired": false,
                    "customFields": [
                        {
                            "name": "Couleur",
                            "type": "ChoiceList",
                            "answer": "Bleue"
                        }
                    ]
                },
                {
                    "name": "Casquette",
                    "amount": 300,
                    "priceCategory": "Fixed",
                    "isRequired": false
                }
            ],
            "tierDescription": "tierDescription",
            "id": 12578,
            "amount": 30000,
            "type": "Membership",
            "initialAmount": 30000,
            "state": "Canceled"
        },
        {
            "order": {
                "id": 12578,
                "date": "2019-12-15T17:27:02+01:00",
                "formSlug": "formulaire-exemple",
                "formType": "Event",
                "organizationName": "name",
                "organizationSlug": "association-exemple",
                "formName": "name",
                "meta": {
                    "createdAt": "2019-12-15T17:27:02+01:00",
                    "updatedAt": "2019-12-15T17:27:02+01:00"
                },
                "isAnonymous": false,
                "isAmountHidden": false
            },
            "payer": {
                "email": "john.doe@test.com",
                "address": "23 rue du palmier",
                "city": "Paris",
                "zipCode": "75000",
                "country": "FRA",
                "company": "Hello Asso",
                "firstName": "John",
                "lastName": "Doe"
            },
            "payments": [
                {
                    "cashOutState": "Transfered",
                    "shareAmount": 1000,
                    "id": 159875,
                    "amount": 11000,
                    "date": "2019-12-15T17:27:02+01:00",
                    "paymentMeans": "Card",
                    "state": "Authorized",
                    "meta": {
                        "createdAt": "2019-12-15T17:27:02+01:00",
                        "updatedAt": "2019-12-15T17:27:02+01:00"
                    }
                }
            ],
            "user": {
                "firstName": "John",
                "lastName": "Doe"
            },
            "priceCategory": "Fixed",
            "tierDescription": "tierDescription",
            "id": 12579,
            "amount": 1000,
            "type": "Donation",
            "initialAmount": 1000,
            "state": "Processed"
        }
    ],
    "pagination": {
        "pageSize": 20,
        "totalCount": 1,
        "pageIndex": 1,
        "totalPages": 1,
        "continuationToken": "201801221052087"
    }
}

Maintenant, il ne vous reste plus qu’à mettre les mains dedans. Notre github contient quelque exemple de code : https://github.com/helloasso il est également ouvert au fork et à l’ouverture d’issues. La documentation de l’API est également précieuse : https://api.helloasso.com/v5/swagger/ui/index

Nous avons hâte de voir ce que vous allez créer avec notre API, n’hésitez pas à contacter l’équipe pour obtenir un peu d’aide ou tout simplement pour partager vos créations !

]]>