Go
Le module wajub-go, ses ressources et le contexte accepté par chaque appel.
wajub-go constitue la partie serveur d'une intégration Wajub. Il conserve votre clé secrète,
crée des paiements, lit leur statut réel et vérifie les signatures des webhooks.
github.com/wajubhq/wajub-go
Stable · GAGo modules
- Version
- v1.2.1
- Runtime
- Go 1.22+
Couvre
- Paiements
- Facturation
- Transferts
- Sync
- Shield
- Taxes
Installation
go get github.com/wajubhq/wajub-goLe chemin d'import est github.com/wajubhq/wajub-go et le package s'appelle wajub : l'import n'a
donc pas besoin d'alias.
Créer le client
New renvoie une erreur et utilise les variables d'environnement en repli. Dans le cas courant,
vous pouvez donc passer une Config vide.
package wajubclient
import (
"log"
"github.com/wajubhq/wajub-go"
)
// Client is built once at start-up and shared. It is safe for concurrent use.
var Client *wajub.Client
func init() {
// Reads WAJUB_API_KEY and WAJUB_WEBHOOK_SECRET when Config leaves them empty.
c, err := wajub.New(wajub.Config{})
if err != nil {
log.Fatalf("wajub: %v", err)
}
Client = c
}| Champ | Rôle |
|---|---|
APIKey | Votre clé sk. ou sk_test.. Utilise WAJUB_API_KEY en repli |
WebhookSecret | Le secret whsec_. Utilise WAJUB_WEBHOOK_SECRET en repli |
HTTPClient | Votre propre *http.Client, pour un proxy, le traçage ou les tests |
MaxNetworkRetries | Le nombre de nouvelles tentatives après un échec, sous forme de *int. nil conserve la valeur par défaut, 0 désactive les nouvelles tentatives |
Timeout | Une time.Duration. Ce champ est ignoré si HTTPClient est défini : configurez alors le délai sur votre propre client |
New renvoie wajub: api key is required lorsque ni le champ ni la variable ne contient de clé.
Une mauvaise configuration échoue donc au démarrage.
Le premier appel
Chaque méthode accepte d'abord un context.Context. Passez le contexte de la requête. Ainsi,
une API lente est annulée avec la requête au lieu de bloquer la goroutine.
payment, err := Client.Payments.Create(ctx, map[string]any{
"amount": 25000,
"currency": "XAF",
"email": "amina@example.com",
"description": "Order 4172",
"reference": "order-4172",
"callback": "https://shop.example.com/complete",
}, nil)
if err != nil {
return fmt.Errorf("create payment: %w", err)
}
http.Redirect(w, r, payment.AuthorizationURL, http.StatusSeeOther)Les paramètres sont transmis sous forme de map[string]any avec les noms de champs exacts de l'API.
Le dernier argument est *RequestOptions, et sa valeur habituelle est nil.
Payment est une structure limitée, Raw contient le reste
Payment décode six champs : ID, Status, Amount, Currency, AuthorizationURL et
AuthorizationToken. Tout le reste de la réponse de l'API, y compris reference et created_at,
se trouve dans payment.Raw sous forme de map[string]any. Utilisez Raw au lieu de supposer qu'un champ existe.
reference, _ := payment.Raw["reference"].(string)
createdAt, _ := payment.Raw["created_at"].(string)Amount est un nombre entier
Amount est un int64, ce qui convient au XAF, au XOF, au NGN et à toutes les devises sans
décimales. Pour une devise avec décimales, comme 12,50 GHS, lisez
payment.Raw["amount"].(float64).
Toutes les ressources du client
| Service | Méthodes |
|---|---|
| client.Global | Ping, Channels, Countries, Currencies |
| client.Payments | Create, Retrieve, List, Cancel, Process, ProcessSplit, ListRefunds |
| client.Customers | Create, Retrieve, Update, Delete, List, Block, Unblock, Activate, Deactivate, ListTaxIDs, CreateTaxID, DeleteTaxID |
| client.Refunds | Create, Retrieve, List |
| client.Transfers | Create, Retrieve, List |
| client.Beneficiaries | Create, Retrieve, Update, Delete, List |
| client.Links | Create, Retrieve, Update, Delete, List |
| client.Invoices | Create, Retrieve, Update, Delete, List, Send, MarkPaid, Cancel |
| client.Accounts | Create, Retrieve, Update, Delete, List, RegenerateToken |
| client.WebhookEndpoints | Create, Retrieve, Update, Delete, List, RotateSecret |
| client.Balance | Retrieve |
| client.Events | List, Retrieve, Resend |
| client.Disputes | List, Retrieve, SubmitEvidence, Accept, Close, SendMessage |
| client.Identity | Resolve, Validate |
| client.Tax | GetSettings, UpdateSettings, Rates, Calculate, Reports, ListCodes, RetrieveCode, ListRegistrations, CreateRegistration, RetrieveRegistration, UpdateRegistration, DeleteRegistration, Jurisdictions, Thresholds, ThresholdAlerts |
| client.Shield | GetSettings, UpdateSettings, Stats, ListBlocklist, AddToBlocklist, RemoveFromBlocklist |
| client.Webhooks | ConstructEvent |
- La vérification de signature de
webhookss'exécute en local, sans appel HTTP. Toutes les autres ressources appellent l'API REST. links,invoices,taxetshieldne fonctionnent qu'en live. Une clé sandbox reçoit403 This feature is only available in live mode.sur chacune de leurs méthodes.refundsettransfersne proposent que la création, la lecture et la liste.
Parcourir une liste par pages
page, err := Client.Payments.List(ctx, map[string]any{
"status": "succeeded",
"per_page": 50,
})
if err != nil {
return err
}
for _, payment := range page.Data {
log.Println(payment["id"], payment["amount"])
}
if page.HasMore {
page, err = page.GetNextPage(ctx)
}
// Or collect every page at once.
all, err := page.AutoPagingIter(ctx)page.Data est un []map[string]any. Les lignes se lisent donc par clé. AutoPagingIter simplifie
le parcours, mais consomme plus de mémoire, car il conserve toutes les lignes avant de les renvoyer.
Idempotence et nouvelles tentatives
Les requêtes en échec sont retentées automatiquement, et une nouvelle tentative ne crée jamais rien en double. Si la même commande peut être soumise à nouveau, passez votre propre clé.
payment, err := Client.Payments.Create(ctx, params, &wajub.RequestOptions{
IdempotencyKey: "order-" + orderID,
})Pour désactiver les nouvelles tentatives, faites pointer MaxNetworkRetries vers zéro.
zero := 0
c, err := wajub.New(wajub.Config{MaxNetworkRetries: &zero})Agir pour un compte connecté
_, err := Client.Payments.Create(ctx, params, &wajub.RequestOptions{
Sync: seller.WajubAccountID,
})Sync reçoit l'identifiant du compte connecté. La configuration est présentée dans Sync.
Webhooks
ConstructEvent vérifie la signature et renvoie l'événement analysé. Il accepte le corps sous forme
d'octets, exactement tel qu'il a été livré.
func WajubWebhook(w http.ResponseWriter, r *http.Request) {
body, err := io.ReadAll(r.Body)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
return
}
event, err := Client.Webhooks.ConstructEvent(
body,
r.Header.Get("X-Wajub-Signature"),
r.Header.Get("X-Wajub-Timestamp"),
0, // 0 means the default 300 second window
)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
return
}
if event["event"] == "payment.succeeded" {
go fulfil(event["data"])
}
w.WriteHeader(http.StatusOK)
}Le nom de l'événement se trouve dans event, pas dans type. Le résultat est un map[string]any,
vous devez donc le lire par clé. Consultez Vérification de signature.
Erreurs
Les erreurs sont des types pointeurs qui enveloppent une WajubError commune. Utilisez donc
errors.As pour les examiner.
payment, err := Client.Payments.Create(ctx, params, nil)
if err != nil {
var invalid *wajub.InvalidRequestError
if errors.As(err, &invalid) {
// invalid.Errors is map[string]string keyed by field name
return c.JSON(http.StatusUnprocessableEntity, invalid.Errors)
}
var limited *wajub.RateLimitError
if errors.As(err, &limited) {
w.Header().Set("Retry-After", strconv.Itoa(limited.RetryAfter))
w.WriteHeader(http.StatusServiceUnavailable)
return nil
}
var conn *wajub.APIConnectionError
if errors.As(err, &conn) && errors.Is(conn, context.DeadlineExceeded) {
return ErrPaymentTimedOut
}
return fmt.Errorf("wajub: %w", err)
}| Type | Cas de retour |
|---|---|
*AuthenticationError | 401 |
*PermissionError | 403 |
*NotFoundError | 404 |
*InvalidRequestError | 400 et 422 |
*RateLimitError | 429, avec RetryAfter en secondes |
*WajubError | Tout autre statut. Cette structure est aussi intégrée à toutes les erreurs ci-dessus |
*APIConnectionError | Aucune réponse. errors.Is atteint la cause, un délai dépassé par exemple |
*WebhookSignatureVerificationError | Un webhook dont la signature n'a pas été vérifiée |
Toutes contiennent Message, Code, HTTPStatus, Errors et Raw.
Débiter sans page hébergée
payment, err := Client.Payments.Create(ctx, map[string]any{
"amount": 25000,
"currency": "XAF",
"phone": "+237670000000",
}, nil)
if err != nil {
return err
}
_, err = Client.Payments.Process(ctx, payment.ID, map[string]any{
"channel": "cm.mtn",
"phone": "+237670000000",
}, nil)Un canal suit le format country.operator. Ainsi, cm.mtn désigne MTN Mobile Money au Cameroun.
La liste complète se trouve dans Mobile Money. Le payeur
doit toujours approuver l'opération sur son téléphone. Le résultat arrive donc par webhook.
Effectuer un payout
transfer, err := Client.Transfers.Create(ctx, map[string]any{
"amount": 100000,
"currency": "XAF",
"beneficiary": map[string]any{
"name": "Amina Diallo",
"channel": "cm.mtn",
"phone": "+237670000000",
},
"reference": "payout-892",
}, &wajub.RequestOptions{IdempotencyKey: "payout-892"})beneficiary accepte aussi l'identifiant ben_… d'un bénéficiaire enregistré. Cette forme est
préférable dès que vous payez plusieurs fois la même personne.
Développement local
wajub listen --forward-to localhost:8080/webhooks/wajub
wajub trigger payment.succeededPour en savoir plus, consultez la CLI.
Pages associées
- Démarrage rapide des SDKsLe même premier appel dans cinq langages, présentés côte à côte.
- WebhooksTous les événements et leurs garanties de livraison.
- Conventions de nommage des SDKsPourquoi Go expose du CamelCase sur un protocole en snake_case.
- IdempotenceCe que protège une clé et pendant combien de temps.
- Gestion des erreursLes échecs à retenter et ceux à présenter à l'utilisateur.
- Vue d'ensemble de la référence APILes endpoints utilisés par chaque méthode ci-dessus.