Packages d’extension
Certaines intégrations sont des produits à part entière — des flux bancaires avec les identifiants du prestataire, la facturation électronique auprès des API gouvernementales. REDA Financials les structure en packages d’extension : le package principal définit un contrat abstrait global, l’extension l’implémente, et un enregistrement de configuration relie les deux. Deux contrats de base sont livrés aujourd’hui, ainsi que trois interfaces de capacité facultatives qu’un prestataire n’implémente que s’il en a besoin.
Fonctionnement du modèle
Les deux contrats suivent la même architecture, et les en-têtes des deux classes la décrivent dans les mêmes termes : « The core package resolves the provider at runtime via: Type.forName(setup.Provider_Class__c).newInstance() ». Aucun des deux contrats n’impose un prestataire en dur. Il y a quatre composants, chacun avec un responsable clairement identifié :
| Composant | Responsable | Rôle |
|---|---|---|
Contrat abstrait — refin.BankFeedProvider / refin.EInvoiceProvider | Package principal | Classes abstraites globales (toutes deux marquées @since 2026-05-28) qui définissent les signatures des méthodes et la forme des résultats. Global = partie de la surface stable du package géré. |
| Implémentation | Package d’extension (ou votre organisation) | Une classe concrète qui étend le contrat ; elle contient toute la logique d’API propre au prestataire. Selon la documentation des deux classes : « Authentication and API credentials are managed entirely by the extension package. » |
Enregistrement de configuration — refin__Bank_Feed_Setup__c / refin__E_Invoice_Setup__c | Votre administrateur | Enregistre le nom pleinement qualifié de la classe d’implémentation dans Provider_Class__c, ainsi que les paramètres d’environnement, d’activation et de périmètre. |
Objet de résultat — BankFeedResult / EInvoiceResult | Package principal | La classe interne globale que renvoie chaque méthode, afin que les résultats soient enregistrés de manière uniforme, quel que soit le prestataire. |
refin.BankFeedProvider — relevés bancaires automatisés
- Le contrat : trois méthodes que fournit une implémentation —
connect(le processus de liaison du prestataire),sync(récupérer les transactions depuis le curseur et livrer les lignes de relevé),disconnect(révoquer l’accès). Les résultats sont renvoyés sous une forme standard (réussite, transactions importées, curseur mis à jour, solde, erreur). - Le câblage : un enregistrement Configuration de flux bancaire par compte connecté — nom du prestataire, environnement (Sandbox/Production), nom de la classe d’implémentation, curseur et statut de synchronisation (Actif / En pause / Erreur / Déconnecté), dernière synchronisation. Les configurations se trouvent sur la page d’enregistrement du compte bancaire.
- Les identifiants restent dans l’extension — le package principal ne voit jamais les jetons du prestataire ; l’extension gère son authentification de bout en bout.
- Le bénéfice : les relevés arrivent sous forme de relevés bancaires ordinaires (avec la source d’import renseignée en conséquence) et suivent le même rapprochement des lignes et le même rapprochement bancaire qu’un fichier CSV — l’automatisation en amont, un processus familier en aval.
Le contrat de flux bancaire, méthode par méthode
Chaque méthode reçoit l’enregistrement de configuration comme contexte. La documentation de la classe décrit le modèle : « Each method receives the Bank_Feed_Setup__c record as the primary context — the provider reads the linked bank account and its own credentials to execute sync logic. »
global abstract BankFeedResult sync(refin__Bank_Feed_Setup__c setup);
global abstract BankFeedResult connect(
refin__Bank_Feed_Setup__c setup,
Map<String, String> params
);
global abstract void disconnect(refin__Bank_Feed_Setup__c setup);
| Méthode | Ce que le contrat en attend (d’après la documentation de la classe) |
|---|---|
sync | « The provider fetches new transactions from the bank API (using the sync cursor from the setup record), converts them to Bank Statement Lines via the core package's internal import path, and returns the updated cursor for persistence. » La documentation précise qu’elle est « Called by the core scheduled batch job » — voir la mise en garde sur l’orchestration ci-dessus ; la partie planification dépend de la version du package principal. |
connect | Lance le processus de liaison d’un nouveau compte bancaire. « Provider-specific: may involve OAuth redirect, token exchange, or API key validation. The params map carries provider-specific connection parameters (e.g. public_token for Plaid, link_id for Belvo). » |
disconnect | « Revokes access tokens, removes webhooks, and cleans up provider-side resources. The core package updates the setup record's Sync_Status to Disconnected after this method completes. » |
BankFeedResult est une classe interne globale — chaque membre est @AuraEnabled global, et le constructeur initialise success à false et transactionsImported à 0 ; une implémentation doit donc mériter sa réussite :
| Membre | Type | Signification |
|---|---|---|
success | Boolean | Indique si l’opération s’est terminée. Valeur par défaut : false. |
transactionsImported | Integer | Nombre de transactions livrées par la synchronisation. Valeur par défaut : 0. |
updatedCursor | String | La nouvelle position incrémentale, renvoyée « for persistence » afin que la prochaine synchronisation reprenne là où celle-ci s’est arrêtée. |
currentBalance | Decimal | Le solde du compte communiqué par le prestataire au moment de la synchronisation. |
errorMessage | String | Le détail honnête de l’échec lorsque success vaut false. |
refin__Bank_Feed_Setup__c — référence des champs
| Champ | Contenu |
|---|---|
Bank_Account__c | Le compte bancaire REDA desservi par ce flux — une configuration par compte connecté. |
Provider_Class__c (libellé « Classe du fournisseur ») | Le cœur du câblage. Description textuelle : « Fully qualified Apex class name that extends refin.BankFeedProvider… Extension packages register their implementation here (e.g. plaid.PlaidBankFeedProvider). » |
Provider__c | Simple nom d’affichage — selon sa propre description, « The actual integration logic is determined by Provider_Class__c, not this field. » |
Environment__c | L’environnement du prestataire ciblé par la connexion (Sandbox / Production). |
Is_Active__c | Indique si le flux participe à la synchronisation. |
Sync_Cursor__c | La position incrémentale du prestataire — sync la lit et renvoie la valeur mise à jour dans BankFeedResult.updatedCursor. |
Sync_Status__c | Liste de sélection : Actif / En pause / Erreur / Déconnecté. L’aide de la valeur Erreur est explicite : « Error: last sync failed — check Audit Log for details. » |
Last_Sync__c | La date de la dernière synchronisation du flux. |
Provider_Account_Id__c | L’identifiant, côté prestataire, du compte lié. |
Un squelette d’implémentation
Voici la forme d’un prestataire — l’équipe de Meridian en esquisse un pour l’API directe de sa banque américaine. Seuls les éléments du contrat sont réels ; les commentaires indiquent où placer la logique de votre prestataire :
global class MeridianBankFeedProvider extends refin.BankFeedProvider {
global override refin.BankFeedProvider.BankFeedResult connect(
refin__Bank_Feed_Setup__c setup, Map<String, String> params) {
refin.BankFeedProvider.BankFeedResult result =
new refin.BankFeedProvider.BankFeedResult();
// Échangez les paramètres de connexion (p. ex. public_token) avec le
// prestataire. Stockez les identifiants dans VOTRE package — jamais dans les objets du package principal.
result.success = true;
return result;
}
global override refin.BankFeedProvider.BankFeedResult sync(
refin__Bank_Feed_Setup__c setup) {
refin.BankFeedProvider.BankFeedResult result =
new refin.BankFeedProvider.BankFeedResult();
// 1. Récupérez les transactions depuis setup.refin__Sync_Cursor__c
// 2. Livrez-les sous forme de lignes de relevé bancaire
// 3. Renvoyez le nouveau curseur pour qu’il puisse être enregistré
result.transactionsImported = 42;
result.updatedCursor = 'cursor-after-this-batch';
result.success = true;
return result;
}
global override void disconnect(refin__Bank_Feed_Setup__c setup) {
// Révoquez les jetons et les webhooks côté prestataire ; le package
// principal passe Sync_Status à Disconnected une fois cette méthode terminée.
}
}
refin.EInvoiceProvider — facturation électronique réglementaire
- Le contrat :
submit(construire la charge utile propre à la juridiction à partir du document et appeler l’administration fiscale),checkStatus(interroger les administrations asynchrones),cancel(des règles propres à chaque juridiction s’appliquent — la note du contrat cite elle-même le délai de 24 heures au Brésil et l’acceptation par le destinataire au Mexique). - Le câblage : une Configuration de facturation électronique par entité/pays — prestataire, environnement, classe d’implémentation, types de documents concernés, et une option Soumission auto à la comptabilisation ; chaque tentative de soumission est enregistrée sous forme d’Historique de facturation électronique sur le document (statut, UUID et numéro fiscaux, charges utiles, réponse, nombre de tentatives) — la piste d’audit de conformité qu’attendent les autorités de contrôle.
- Note de disponibilité : les historiques et les listes associées sont visibles dans toute l’application (vous avez déjà vu Suivis de facture électronique sur les pages des factures) ; la soumission réelle nécessite l’extension du prestataire pour votre juridiction — vérifiez auprès de REDA ce qui est disponible pour votre pays.
Le contrat de facturation électronique, méthode par méthode
| Méthode | Signature | Contrat |
|---|---|---|
| submit | global abstract EInvoiceResult submit(refin__E_Invoice_Trail__c trail, SObject sourceDocument) | Le document source est un refin__Sales_Invoice__c ou un refin__Purchase_Invoice__c ; le prestataire construit le XML/JSON propre à la juridiction et appelle l’API gouvernementale ; le package principal met à jour l’historique à partir du résultat |
| checkStatus | global abstract EInvoiceResult checkStatus(refin__E_Invoice_Trail__c trail) | Interrogation des administrations asynchrones qui répondent plus tard |
| cancel | global abstract EInvoiceResult cancel(refin__E_Invoice_Trail__c trail) | « Jurisdiction-specific rules apply (e.g. Brazil NF-e cancellation within 24h, Mexico CFDI cancellation with receiver acceptance). » |
Les trois renvoient un EInvoiceResult — membres : success, status, fiscalUUID, fiscalNumber, responseCode, responseMessage, xmlPayload, responsePayload, errorMessage.
Les objets de facturation électronique — référence des champs
Champ de refin__E_Invoice_Setup__c | Rôle |
|---|---|
Entity__c · Country_Code__c · Environment__c · Is_Active__c | Quelle société, quelle juridiction, test ou production, activé ou non |
Provider__c | Simple nom d’affichage |
Provider_Class__c | La classe Apex, désignée par son nom pleinement qualifié, qui étend refin.EInvoiceProvider (l’exemple du champ lui-même : sovos.SovosEInvoiceProvider) |
Document_Types__c | Sélection multiple : Facture de vente / Avoir / Facture d’achat / Avoir fournisseur — d’après sa description : « core package checks this field when determining whether to create an E-Invoice Trail record » |
Auto_Submit_On_Post__c | Texte exact : « When checked, posting a Sales Invoice or Purchase Invoice automatically creates an E-Invoice Trail record in Draft status, triggering the asynchronous submission process. When unchecked, e-invoice submission must be initiated manually via a quick action or API call. » |
L’enregistrement refin__E_Invoice_Trail__c est la ligne d’audit propre à chaque soumission : des champs de relation vers la configuration et la facture source, Document_Type__c, Status__c, Fiscal_UUID__c, Fiscal_Number__c, XML_Payload__c, les champs de réponse (Response_Code__c/Message__c/Payload__c/Date__c), Submitted_Date__c, Retry_Count__c et Provider_Document_Id__c — la chaîne de preuves qu’attendent les autorités de contrôle, une ligne par tentative. Status__c compte huit valeurs : Brouillon, Prêt à envoyer, En file d’attente, Soumis, Accepté, Rejeté, Annulé, Erreur.
Interfaces de capacité facultatives — comment un contrat figé continue de s’enrichir
refin.EInvoiceProvider a été livré en tant que classe abstraite globale dans une version Managed-Released, ce qui signifie qu’aucune méthode ne pourra jamais y être ajoutée. Plutôt que de figer le framework avec elle, REDA ajoute les nouveaux comportements sous forme d’interfaces globales distinctes, ce que Salesforce autorise. Un prestataire étend EInvoiceProvider et implémente les interfaces de capacité qu’il prend en charge ; le package principal détecte cette prise en charge avec instanceof et ignore simplement la capacité pour les prestataires qui ne l’implémentent pas.
| Interface | Méthodes | À implémenter lorsque |
|---|---|---|
refin.EInvoiceQueueProvider | drain(setup, maxRecords) renvoyant List<EInvoiceEvent> ; acknowledge(setup, events) | Votre administration fiscale répond via une file de notifications plutôt que par une réponse synchrone. Un seul drain couvre les deux sens — les mises à jour de statut sortantes et les documents fournisseurs entrants — distingués par EInvoiceEvent.processType. Il n’existe délibérément aucune méthode distincte de « récupération des entrants ». |
refin.EInvoiceDocumentProvider | fetchDocuments(trail) renvoyant List<EInvoiceDocument> | Vous récupérez les pièces légales (XML signé, réponse de l’administration, représentation imprimable comme le DANFE au Brésil) à la demande plutôt que de les livrer directement dans l’événement. Ignorez-la si vos événements contiennent déjà documents. |
refin.EInvoiceProvisioner | provisionOrganization(setup, args) ; provisionCompany(setup, args) — toutes deux renvoyant EInvoiceProvisionResult | Vous prenez en charge le provisionnement en libre-service des structures de compte côté administration depuis l’application. Il s’agit d’une capacité prévue pour une phase ultérieure — aujourd’hui, ces structures sont créées manuellement par le prestataire de conformité. Elle est déclarée dès maintenant précisément parce qu’une API globale publiée ne peut pas être étendue par la suite. |
acknowledge qu’une fois chaque événement stocké de façon durable — accuser réception d’abord ferait perdre définitivement la notification. Un prestataire ne doit activer aucun mode d’accusé de réception automatique qui devancerait cet appel, et drain ne doit renvoyer que les notifications non acquittées, afin que des appels répétés à drain fassent progresser le traitement.
Les trois types de données qu’elles échangent
refin.EInvoiceEvent— une étape de la vie d’un document auprès de l’administration. Le package principal la rattache à un historique grâce àerpDocumentId, une valeur que votre prestataire inscrit dans l’enveloppe sortante et que l’administration renvoie en écho.statusest normalisé en Accepted, Rejected, Cancelled ou InProgress — le package principal fonde sa logique de nouvelle tentative et d’état final sur cette valeur, jamais sur leresponseCodebrut. L’événement contient aussinotificationId,processType(Outbound / Inbound),responseMessage,fiscalUUID,fiscalNumber, une listedocumentsfacultative,payloadeteventDate.refin.EInvoiceDocument— une pièce :title,fileExtension('xml' ou 'pdf'),base64BodyetdocumentKind('Legal', 'LegalResponse' ou 'Representation'). Le package principal la décode et la joint à l’historique et à la facture source sous forme de fichiers Salesforce, afin que les utilisateurs voient les pièces conformes sur l’enregistrement qu’ils utilisent déjà.refin.EInvoiceProvisionResult—success(par défautfalse),externalId(l’identifiant côté prestataire) etmessage.
documentKind, processType et status sont tous de type String plutôt que des énumérations — délibérément, afin que l’ensemble des valeurs autorisées puisse s’étendre sans casser une signature globale figée. N’écrivez pas de switch exhaustif sur ces valeurs ; traitez celles que vous connaissez et laissez passer les autres.
Erreurs courantes à éviter
| Erreur | Pourquoi c’est problématique | À faire plutôt |
|---|---|---|
Fautes de frappe dans Provider_Class__c | La résolution à l’exécution se fait par le nom — une chaîne erronée signifie que le prestataire ne s’exécute tout simplement jamais | Copiez le nom pleinement qualifié (namespace.ClassName) depuis la documentation de l’extension ; testez la connexion immédiatement |
Compter sur Provider__c pour changer de prestataire | C’est un libellé d’affichage ; « The actual integration logic is determined by Provider_Class__c, not this field. » | Modifiez Provider_Class__c ; mettez à jour le libellé pour les lecteurs humains |
| Stocker les identifiants de l’administration ou de la banque dans les enregistrements de configuration | Le contrat est explicite : « Authentication and API credentials are managed entirely by the extension package » | Les identifiants résident dans les informations d’identification nommées propres à l’extension, régies par la sécurité de votre organisation |
| Faire reposer le code de votre organisation sur les mécanismes internes d’un package de prestataire | Les packages de prestataires évoluent selon leur propre calendrier — seuls les contrats globaux refin. constituent le point de jonction stable | Intégrez via les enregistrements de configuration et les historiques ; traitez le prestataire comme une boîte noire |
| Présumer des détails d’orchestration sans tester votre version | Le comportement de planification et d’envoi automatique dépend de la version | Vérifiez la planification des synchronisations et le comportement de soumission automatique dans une sandbox, sur vos versions installées, avant de les promettre aux métiers |
Pour les équipes qui développent une extension
- Implémentez la classe abstraite dans l’espace de noms de votre package ; enregistrez le nom pleinement qualifié de la classe dans l’enregistrement de configuration.
- Maîtrisez vos identifiants (informations d’identification nommées dans votre package), votre mappage d’API, votre sémantique d’erreur — renvoyez des résultats honnêtes ; le package principal les affiche dans le Statut de synchronisation et les historiques.
- Développez uniquement en vous appuyant sur le contrat — la surface globale documentée est la seule qui soit stable (les règles).
Pour aller plus loin
- Importer des relevés bancaires — le processus dans lequel arrivent les flux.
- Intégrations AppExchange — la vue d’ensemble de l’écosystème.
- Développement Apex sur REDA — les règles de la surface globale.
Questions fréquentes
Le contrat est conçu pour l’écosystème des agrégateurs (Plaid, Belvo et leurs homologues sont les archétypes qu’il cite). Demandez à REDA quelles extensions de prestataires sont disponibles pour votre région — l’offre évolue plus vite que la documentation.
Le contrat résout la classe d’implémentation par son nom ; une implémentation locale à l’organisation est donc techniquement adressable — une option judicieuse pour une API bancaire sur mesure. Packagez-la correctement si elle est appelée à servir au-delà d’une seule organisation.
Cela dépend du pays (le Brésil, le Mexique, l’Italie et l’Arabie saoudite ouvrent la voie ; beaucoup d’autres suivent). C’est à votre conseiller fiscal de répondre à la question « devons-nous le faire ? » ; la configuration et l’extension du prestataire répondent au « comment ».
BankFeedResult indique success, transactionsImported, updatedCursor (le point où reprendra la prochaine synchronisation), currentBalance et errorMessage — les valeurs par défaut sont honnêtes (success=false, aucune transaction importée), si bien qu’un prestataire négligent ne peut pas annoncer une réussite par accident. EInvoiceResult transporte la réponse de l’administration fiscale : identifiants fiscaux, charges utiles de réponse et texte de l’erreur en cas de refus.
C’est l’état de santé de la connexion en un coup d’œil — Actif / En pause / Erreur / Déconnecté. La consigne du champ lui-même pour l’état problématique : « Error: last sync failed — check Audit Log for details. » La valeur Déconnecté est définie par le package principal une fois la méthode disconnect de votre prestataire terminée.
Cet article vous a-t-il été utile ?