iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more
A team building a platform where every business collects its own customer payments cannot use a single global Paystack client. Oluwafemi Sosami’s account, published on DEV Community in April, describes how that requirement led his team to write github.com/saphemmy/paystack-go, a Go package built around per-tenant credentials, explicit payment flows, and caller-owned retries. This article walks through the design decisions he describes, what each one means for an integrator, and where the claims still need checking against Paystack’s own documentation.
The constraint that started it
The platform in the story is a multi-tenant system. Each business has its own Paystack account, its customers pay that business rather than the platform, and so every outbound request has to be made with that business’s credentials. In a single-merchant app, one secret key loaded at startup is enough. In this setup, the key that authorizes a transaction depends on who the request belongs to.
That is the core reason the package exists. The author did not set out to write an SDK for its own sake; the existing options did not fit the way his system needed to route payments. Everything else in the package follows from that starting point.
Free tools Windows power users keep installed
One-click scans. No signup required.
Clients are built per tenant, not held as a singleton
Because each tenant has its own secret key, the author creates a client for the tenant making the request rather than keeping one global instance. The example he gives is an encrypted credential store with a short-lived cache: when a request arrives, the system looks up the tenant’s key, builds or reuses a client for that tenant, and makes the call.
#1 Best Overall
This is the author’s architecture, not a requirement of Paystack. A single-merchant integration can reasonably keep one client. The pattern matters when credentials differ by customer, and it carries a cost: the credential store and cache become security-sensitive components that need their own rotation and expiry rules.
Interfaces that make the code testable
The package is organized around interfaces. According to the article, New returns a ClientInterface, the service accessors return interfaces, and the HTTP operations sit behind a Backend interface. A mock backend can be supplied with WithBackend, so application code can be tested without a network connection to Paystack.
The author reports that the CI pipeline ran thousands of tests with zero real Paystack API calls. That is the team’s own description of its test setup, not an independently audited result. Sandbox tests are opt-in through an integration build tag, so they run only when that tag is set.
Do these 3 things before closing this tab:
1Scan for outdated or missing drivers - takes under a minute2Repair Windows errors before they cause bigger problems3Fix the driver behind crashes, sound loss and screen glitchesTwo payment flows that behave differently
The article’s most useful distinction is between transaction initialization and charge creation. They are not interchangeable, and application code should not treat them as the same kind of call.
| Aspect | Transaction initialization | Charge creation |
|---|---|---|
| What comes back | A checkout URL for the customer to visit | A status that determines the next action |
| Who drives the payment | Paystack’s hosted checkout page | Your code, step by step |
| Possible next steps | Redirect the customer and wait for the outcome | PIN, OTP, phone, birthday, polling, or completion |
| Typical use in the article | Standard checkout | Mobile money and other stateful flows |
Charge creation is a state machine. The author’s point is that the status returned after each call tells the caller what to do next, so the calling code has to handle every state it can receive, including states that require polling before the outcome is known.
On raw card data, the author is direct: entering card details in your own code is appropriate only if you have PCI scope for that work. Otherwise, use authorization codes or Paystack’s standard checkout. Check Paystack’s current requirements before relying on either path, since the article did not verify them against the live API.
Rank #4
Amounts are integer kobo, and currency is your problem
Amount fields in the package use integer kobo, where 1 NGN equals 100 kobo. The SDK does not convert currencies. If your application handles more than one currency, the conversion, rounding, and display logic belong to your code, not the package.
The Tool Desk
Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Retries belong to the caller
The package does not retry requests. The author states it plainly: “The SDK doesn’t retry anything. Ever.” Retry policy, including backoff, which errors justify a retry, and how many attempts to make, is left to the calling application. This is a deliberate boundary, and it matters most for charge creation, where a blind retry could repeat a payment step that has already moved forward.
Best Value
Idempotency keys come from you
Callers can set an idempotency key, and the SDK forwards it in a request header. The SDK does not generate the key. The author suggests a namespace built from tenant, operation, and request identifiers, for example a key that combines the tenant ID, the operation name such as charge, and an ID from your own system. Because the key is yours, it stays stable across retries that your code makes deliberately.
Webhooks are routed by tenant before they are verified
The webhook flow described in the article has four steps:
- Route the incoming request to the correct tenant.
- Retrieve that tenant’s webhook secret.
- Verify the HMAC signature on the request body.
- Parse the event data.
The article also mentions a body-size limit and constants for dispute events. Treat these as the package’s behavior, not as guarantees from Paystack. If you rely on a specific event name or payload shape, confirm it in Paystack’s current webhook documentation.
Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Clear out junk files and repair common Windows errorsFree Scan →Typed errors and framework modules
Errors are typed and expose status-related information, including rate-limit retry timing and the raw response body. The caller decides what to do with them. For HTTP frameworks, the author names separate modules for Gin, Fiber, and Echo. These are distinct software modules, so you import only the one your application uses.
Quick Recap
What the article does and does not establish
- The package is described as MIT licensed. The license and the repository’s current state were not verified against the live repository for this account.
- The design claims about tenant routing, interfaces, idempotency, and webhook verification come from the author. They were not checked against the current Paystack API.
- The testing figures are the team’s own description of its CI setup.
- The article does not compare the package with other Go SDKs, so it offers no basis for ranking them.
Checklist before you adopt this pattern
- Do you hold a separate Paystack secret key per customer? If not, a single shared client is likely simpler.
- Does your code handle every charge status it can receive, not only the success path?
- Do you own currency conversion and rounding?
- Have you written retry rules for each payment operation, with idempotency keys to match?
- Is each webhook verified against the correct tenant’s secret before any event is processed?
- Have you checked the current Paystack documentation for every endpoint and event your code uses?
“
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

