agentsclimarketplace

Paystack

Skill tzone85/sa-fintech-skills/skills/paystack

Integrate Paystack for SA merchants — initialise ZAR transactions, verify HMAC-SHA-512 webhooks, and route split payments without the gotchas that break production.From its SKILL.md

Install
npx -y skills add tzone85/sa-fintech-skills --skill paystack

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

5.3 KB, ~1.1k tokens by cl100k_base, as published. Nobody here has run it

Paystack — South African payment integration

Use when the user is wiring a backend to Paystack for South African card payments. Paystack supports ZAR for SA merchants alongside NGN, GHS, KES, XOF, and EGP. Common operations: initialise a transaction, verify webhooks, verify a transaction reference, route money via split payments.

Triggers

  • "paystack"
  • "init transaction"
  • "verify webhook"
  • "x-paystack-signature"
  • "split payment"
  • "subaccount"
  • "transaction reference"
  • "ZAR payment"

Examples

Canonical rules

Initialise transaction (POST /transaction/initialize):

  • Amount must be in the subunit of the currency. For ZAR that means cents — multiply rand by 100. R150.00 = 15000.
  • Set currency: "ZAR" explicitly. Paystack defaults to the integration's primary currency, which for some accounts is NGN.
  • Returns data.authorization_url — redirect the customer to that URL. Returning status: "success" from initialize means initialisation succeeded, NOT that the customer paid.

Verify transaction (GET /transaction/verify/:reference):

  • The only safe source of truth for whether a customer actually paid.
  • Check data.status === "success" AND data.amount === expectedAmountInCents AND data.currency === "ZAR" before fulfilling.

Webhook signature verify:

  • Paystack sends events with header x-paystack-signature.
  • Signature is HMAC-SHA-512 over the request body, signed with your secret key (sk_live_... or sk_test_...).
  • Use crypto.timingSafeEqual to compare — never === (timing attack).
  • Use the raw request body if at all possible. If using express.json(), the body is already parsed and you must JSON.stringify(req.body) to recreate the bytes Paystack signed; this works in practice but is fragile if either side changes serialisation. Prefer express.raw({ type: 'application/json' }) on the webhook route.

Split payments:

  • Two routes: pre-configured split_code (created via the Splits API) OR ad-hoc subaccount on initialise.
  • bearer: "subaccount" makes the subaccount carry Paystack's transaction fees.

Common mistakes

  • Using parsed req.body directly for HMAC — when middleware re-serialises the object differently from how Paystack serialised it (key ordering, whitespace, unicode escapes), the hash will never match. Either keep the raw bytes (express.raw) or accept that JSON.stringify(req.body) is a brittle workaround that has worked historically but can break silently.
  • Forgetting currency: "ZAR" on initialise — Paystack defaults to the integration's currency, which can silently charge in NGN.
  • Treating initialize's status: "success" as "the customer paid" — it only means initialisation succeeded; the customer is then redirected and may abandon. Always re-verify with /transaction/verify/:reference before fulfilling the order.
  • Comparing signatures with === — leaks timing info. Use crypto.timingSafeEqual on equal-length Buffers.
  • Sending rand instead of cents on initialise — R150 becomes 150 cents (R1.50) charged to the customer.
  • Logging the full webhook body to shared observability — webhook payloads contain PII (cardholder names, masked PANs, IP addresses) that fall under POPIA processing rules. Scrub before sending to Sentry/Datadog.
  • Ignoring the event field on webhooks — Paystack fires events for charge.success, transfer.success, subscription.create, etc. Treating every webhook as a successful charge will double-credit on subscription renewals.

Configuration

Store the secret key in PAYSTACK_SECRET_KEY (or PAYSTACK_TEST_SECRET_KEY for tests). Never commit it. The public key (pk_live_... / pk_test_...) is safe in client-side code.

For SA merchants:

  • Integration currency in the Paystack dashboard → set to ZAR.
  • Webhook URL → https://your-domain/api/paystack/webhook, registered in the dashboard under Settings → API Keys & Webhooks.

See also

What ships with it: 5 files

10.1 KB alongside SKILL.md, 3 of them executable

examples/

Keep looking

Skills are one crate of 326,782. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.