Go SDK
The official PayKore Go SDK.
Module: github.com/paykore/paykore-go
All API calls must be made server-side. Never embed your API key in a binary or any context where it can be extracted.
Installation
go get github.com/paykore/paykore-go
Initialisation
import (
paykore "github.com/paykore/paykore-go"
"github.com/paykore/paykore-go/option"
)
client := paykore.NewClient(
option.WithBaseURL("https://api.paykore.com"),
option.WithToken(os.Getenv("PAYKORE_API_KEY")),
)
For sandbox, swap both the base URL and the key:
client := paykore.NewClient(
option.WithBaseURL("https://sandbox.paykore.com"),
option.WithToken(os.Getenv("PAYKORE_SANDBOX_KEY")),
)
Your API key (sk_live_* or sk_test_*) is sent as Authorization: Bearer <key> on every request. Use sk_test_* keys with the sandbox URL and sk_live_* with production — mismatches return 401.
Wallets
// Create a wallet
wallet, err := client.Wallets.CreateWallet(ctx, &paykore.CreateWalletRequest{
UserRef: "user_123",
Currency: paykore.String("NGN"),
})
// wallet.Status == "pending" initially
// account_number is empty until wallet.activated webhook fires
// Get a wallet and its current balance
result, err := client.Wallets.GetWallet(ctx, &paykore.GetWalletRequest{
ID: "wlt-uuid-here",
})
// result.Wallet — the full Wallet struct
// result.BalanceKobo — current posted balance in kobo
// List a wallet's transactions
txns, err := client.Wallets.ListWalletTransactions(ctx, &paykore.ListWalletTransactionsRequest{
ID: "wlt-uuid-here",
Limit: paykore.Int(20),
})
| Method | Returns |
|---|---|
client.Wallets.CreateWallet(ctx, req) | (*CreateWalletResponse, error) |
client.Wallets.GetWallet(ctx, req) | (*GetWalletResponse, error) |
client.Wallets.ListWalletTransactions(ctx, req) | (*ListWalletTransactionsResponse, error) |
CreateWallet returns 202 Accepted — the wallet exists immediately in pending state, but account_number is empty until the MFB provisions it asynchronously. Listen for the wallet.activated webhook before showing the NUBAN to your user.
Transfers
import "github.com/google/uuid"
// P2P transfer — instant, no NIP fee, synchronous 200 response
transfer, err := client.Transfers.P2PTransfer(ctx, &paykore.P2PTransferRequest{
SourceWallet: "source-wallet-uuid",
DestWallet: "dest-wallet-uuid",
Amount: 500000, // kobo — ₦5,000
Reference: uuid.New().String(),
Description: paykore.String("Payment for order #789"),
})
// Bank transfer — async NIP, returns 202, settles via webhook
payout, err := client.Transfers.BankTransfer(ctx, &paykore.BankTransferRequest{
SourceWallet: "source-wallet-uuid",
DestAccount: &paykore.BankAccount{
BankCode: "058",
AccountNumber: "0123456789",
AccountName: "Ada Okafor",
},
Amount: 1000000, // kobo — ₦10,000
Reference: uuid.New().String(),
})
// payout.Status == "processing"
// Final status arrives via transaction.completed or transaction.failed webhook
| Method | Returns |
|---|---|
client.Transfers.P2PTransfer(ctx, req) | (*Transaction, error) |
client.Transfers.BankTransfer(ctx, req) | (*Transaction, error) |
Payments
// Create a QR payment request (merchant side)
qr, err := client.Payments.CreateQRPayment(ctx, &paykore.CreateQRPaymentRequest{
WalletID: "merchant-wallet-uuid",
Amount: 500000,
Reference: "order_789",
ExpiresInSeconds: paykore.Int(300), // default 5 minutes
})
// qr.QRCode — base64-encoded QR image or payload string
// qr.ExpiresAt — show countdown to user
// Pay a QR request (payer side)
payment, err := client.Payments.PayQRPayment(ctx, &paykore.PayQRPaymentRequest{
QRReference: qr.Reference,
SourceWallet: "payer-wallet-uuid",
})
// Initiate a USSD payment
ussd, err := client.Payments.InitiateUSSDPayment(ctx, &paykore.InitiateUSSDPaymentRequest{
WalletID: "wallet-uuid",
Amount: 500000,
Reference: "order_789",
Phone: "+2348012345678", // E.164 format
})
// ussd.UssdCode — display to user e.g. "*123*456#"
// ussd.SessionID — use to poll status if needed
// ussd.ExpiresAt — 3-minute TTL
// Split payment across multiple wallets
splits, err := client.Payments.SplitPayment(ctx, &paykore.SplitPaymentRequest{
SourceWallet: "source-wallet-uuid",
Reference: "order_456",
Splits: []*paykore.SplitEntry{
{WalletID: "seller-wallet-uuid", Amount: 824000},
{WalletID: "platform-wallet-uuid", Amount: 176000},
},
})
// splits — []*Transaction, one per destination
| Method | Returns |
|---|---|
client.Payments.CreateQRPayment(ctx, req) | (*CreateQRPaymentResponse, error) |
client.Payments.PayQRPayment(ctx, req) | (*Transaction, error) |
client.Payments.InitiateUSSDPayment(ctx, req) | (*InitiateUSSDPaymentResponse, error) |
client.Payments.SplitPayment(ctx, req) | ([]*Transaction, error) |
KYC
// Submit BVN — async, result arrives via kyc.verified or kyc.failed webhook
bvnResult, err := client.KYC.SubmitBVN(ctx, &paykore.SubmitBVNRequest{
UserRef: "user_123",
BVN: "22190000000",
})
// bvnResult.Status == "pending"
// Submit NIN
ninResult, err := client.KYC.SubmitNIN(ctx, &paykore.SubmitNINRequest{
UserRef: "user_123",
NIN: "12345678901",
})
// Get current KYC status for a user
status, err := client.KYC.GetKYCStatus(ctx, "user_123")
for _, v := range status.Verifications {
fmt.Printf("type=%s status=%s\n", v.Type, v.Status)
}
| Method | Returns |
|---|---|
client.KYC.SubmitBVN(ctx, req) | (*SubmitVerificationResponse, error) |
client.KYC.SubmitNIN(ctx, req) | (*SubmitVerificationResponse, error) |
client.KYC.GetKYCStatus(ctx, userRef) | (*KYCStatusResponse, error) |
Webhooks
// Register an endpoint
endpoint, err := client.Webhooks.CreateWebhook(ctx, &paykore.CreateWebhookRequest{
URL: "https://yourapp.com/webhooks/paykore",
Events: []string{
"transaction.completed",
"transaction.failed",
"wallet.activated",
"kyc.verified",
"kyc.failed",
},
})
// List registered endpoints
endpoints, err := client.Webhooks.ListWebhooks(ctx)
// Delete an endpoint
err = client.Webhooks.DeleteWebhook(ctx, "webhook-uuid")
| Method | Returns |
|---|---|
client.Webhooks.CreateWebhook(ctx, req) | (*WebhookEndpoint, error) |
client.Webhooks.ListWebhooks(ctx) | ([]*WebhookEndpoint, error) |
client.Webhooks.DeleteWebhook(ctx, id) | error |
Error handling
All methods return a standard Go error. Cast to *core.APIError to inspect the code:
import (
"errors"
"github.com/paykore/paykore-go/core"
)
transfer, err := client.Transfers.P2PTransfer(ctx, req)
if err != nil {
var apiErr *core.APIError
if errors.As(err, &apiErr) {
switch apiErr.Body.Error {
case "INSUFFICIENT_FUNDS":
// show user their balance
case "VALIDATION_ERROR":
log.Printf("bad request: %s", apiErr.Body.Message)
default:
log.Printf("paykore: %s — %s (http %d)",
apiErr.Body.Error,
apiErr.Body.Message,
apiErr.StatusCode,
)
}
} else {
// network or timeout — retry with backoff
}
}
| Field | Type | Description |
|---|---|---|
apiErr.StatusCode | int | HTTP status code (e.g. 422). |
apiErr.Body.Error | string | Machine-readable code. Branch on this — it's stable across SDK versions. |
apiErr.Body.Message | string | Human-readable description. For logging only. |
Raw response access
To read response headers (e.g. Retry-After on a 429), use .WithRawResponse:
rawResp, err := client.Wallets.WithRawResponse.CreateWallet(ctx, req)
if err != nil {
return err
}
retryAfter := rawResp.Header.Get("Retry-After")
wallet := rawResp.Body // *CreateWalletResponse
Next steps
- Python SDK → — The Python equivalent.
- Webhooks: Verifying Signatures → — Go signature verification example.
- API Reference → — Full endpoint documentation.