Search
Intermediate Certificate on pass

Vendor Payments (M-Pesa & Bank)

Pay your vendors for approved purchase orders by M-Pesa B2C, M-Pesa B2B, Paystack bank transfer, or a manual cash/cheque/EFT record — each posted back onto the PO balance automatically. Non-custodial: money leaves the business's own account, never AWRA's.

5 lessons 45 min 5-question assessment 75% to pass

What you’ll learn

  • Explain the non-custodial, outgoing model and how it differs from Sales Payments and from subscription billing
  • Choose the right method — M-Pesa B2C, B2B, Paystack transfer, or manual — for a given vendor
  • Describe how a confirmed payment posts to the PO balance and why the approved-PO guard matters
  • Reason about idempotency, timeouts, balance checks, and the security that protects every payout

Course content

5 lessons · 45 min of reading
01
Lesson 1 of 5 Reading 8 min

The model: pay vendors on your own rails

Vendor Payments solves the inverse of Sales Payments. Where sales collection is money coming in from customers, vendor payments are money going out to suppliers — and the pain it removes is the app-switching dance most finance teams know too well: a purchase order is approved in one system, then a cashier opens M-Pesa or online banking to actually pay, and afterwards someone re-keys the amount, the reference, and the new balance by hand. AWRA collapses that into one action: you initiate the payment from the approved PO itself, and the confirmed payment posts back onto that PO automatically. Internalise the direction first, because it shapes every other decision in this course — this is the tenant paying its vendor, not a customer paying the tenant.

Like sales collection, vendor payments are non-custodial, but the credentials in play are different. M-Pesa payouts use the same TenantMpesaConfig the tenant already stores, but they rely on the initiator credentials (initiator name and the password used to build a security credential) rather than the STK passkey — because sending money is a B2C/B2B operation, not a customer prompt. Paystack transfers use the same TenantPaystackConfig secret key as card collection, but hit the Transfers API and draw from the tenant's Paystack wallet balance rather than the charge/subaccount flow. In every case the money leaves the tenant's own M-Pesa shortcode or Paystack wallet straight to the vendor; AWRA only initiates the request and records the outcome.

One guard governs the whole feature: you can only pay a purchase order that is approved and still has a balance due. This is enforced at the controller, not merely hidden in the UI, so a draft, pending, or cancelled PO cannot be paid even if someone crafts the request directly. The mental model to carry forward is a four-step loop that mirrors sales but points outward: the PO is approved, you initiate a payment by one of four methods, the provider confirms (or you record a manual payment), and the payment posts onto the PO — updating amount paid, balance due, and status without anyone re-keying a figure.

Key takeaways

  • Vendor Payments are outgoing (tenant → vendor) — the inverse of Sales Payments and unrelated to AWRA subscription billing.
  • M-Pesa payouts use the tenant's TenantMpesaConfig initiator credentials (not the STK passkey); transfers use TenantPaystackConfig on the Transfers API.
  • It is non-custodial: funds leave the tenant's own shortcode or Paystack wallet directly to the vendor.
  • Only an approved PO with a balance due can be paid, and that guard is enforced server-side, not just in the UI.
02
Lesson 2 of 5 Practice 11 min

M-Pesa B2C and B2B: phone vs paybill/till

M-Pesa gives you two outbound paths, and the choice is about what the vendor is registered as. B2C ("business to customer") pays a vendor's personal M-Pesa phone number — the common case for small suppliers and individuals. Under the hood AWRA calls Daraja's B2C payment request with the tenant's shortcode as the sending party and the vendor's phone as the receiving party. B2B ("business to business") pays a company vendor's registered paybill or till number instead; it carries identifier types that distinguish a paybill from a till, and it embeds the PO number as the account reference so the vendor can reconcile the receipt on their own side. If the vendor gives you a phone number, that is B2C; if they give you a paybill or till, that is B2B.

Both are asynchronous, and that asynchrony is the most important operational fact to teach. When you initiate, AWRA creates a pending transaction and sends the request to Daraja; Daraja does not return the final result inline. Instead it calls back to two URLs you registered: a Result URL with the definitive success or failure, and a Timeout/Queue URL when it could not produce a definitive result in time. AWRA matches the callback to the pending transaction (by the originator conversation id), and only then does it finalise — a status poller on the PO page keeps the cashier informed in the meantime. Because the result arrives out-of-band, the cashier should wait for confirmation rather than assuming the moment they clicked send.

Sending money also requires a credential the STK flow never needs: a security credential, generated fresh on every call by RSA-encrypting the stored initiator password against Safaricom's public certificate. AWRA never stores the encrypted credential itself — it rebuilds it each time — which is why B2C and B2B are realistically production capabilities that need the initiator name, initiator password, and the certificate configured. The same security-credential logic already powers M-Pesa reversals, so the pattern is shared rather than reinvented.

Key takeaways

  • B2C pays a vendor's personal phone; B2B pays a registered paybill or till, with the PO number as the account reference.
  • Both are async: AWRA creates a pending transaction and finalises only on Daraja's Result URL callback (or marks a Timeout).
  • A status poller keeps the cashier updated — wait for the callback, do not assume success on click.
  • Payouts need a security credential rebuilt each call from the initiator password and Safaricom's public certificate (never stored).
03
Lesson 3 of 5 Reading 8 min

Paystack transfers: pay any bank account

When a vendor wants money in a bank account, AWRA uses Paystack Transfers — a completely separate API from the charge/subaccount flow used to collect card payments, even though it shares the same tenant secret key. The flow runs in stages. The first time you pay a given vendor bank account, AWRA resolves the account (validating the bank code and account number and returning the real account name to confirm), then creates a transfer recipient and stores the returned recipient code in a small vendor_paystack_recipients table. That recipient is reused for every future payment to the same vendor account, so the validation step happens once, not every time.

Before initiating, AWRA checks the tenant's live Paystack wallet balance and surfaces it in the Pay Vendor modal. This matters because transfers draw down that wallet (funded by card collections or a manual top-up), so a payout can only go out if the balance covers it. If funds are short, AWRA blocks the transfer with a clear message rather than letting it fail silently — the cashier can top up or switch to M-Pesa. When you confirm, AWRA initiates the transfer (amounts are sent in kobo, so a KES figure is multiplied by 100) and stores the returned transfer code, which is how the later webhook is matched back to the pending transaction.

Settlement is confirmed by webhook, not by the initiation response. Paystack sends a server-to-server event — transfer.success, transfer.failed, or transfer.reversed — that AWRA verifies with an HMAC signature computed using that tenant's secret key before acting. A successful transfer posts onto the PO and triggers the vendor remittance email; a failure logs the reason without charging the PO; a reversal backs the payment out, re-opening the balance if it had already been posted. This vendor webhook is deliberately distinct from the sales-collection Paystack webhook — different endpoint, different log table — so outgoing and incoming operations never cross.

Key takeaways

  • Paystack Transfers is a separate API from card collection; first payment resolves the account and stores a reusable recipient code.
  • The tenant's wallet balance is shown and enforced before initiating — a short balance blocks the transfer instead of failing silently.
  • Amounts are sent in kobo (KES × 100); the returned transfer code matches the later webhook to the transaction.
  • transfer.success / failed / reversed arrive by HMAC-verified webhook on a vendor-specific endpoint and log table, separate from sales.
04
Lesson 4 of 5 Practice 10 min

Posting to the PO, idempotency, and timeouts

Posting is the payoff. When a payment is confirmed — by a Daraja result, a Paystack transfer.success, or a manual record — AWRA sums the successful payments against the purchase order and updates three fields: amount paid, balance due (the PO total minus what is paid), and payment status. Status is derived, not typed: it becomes paid when the total is covered, partial when some has been paid, and otherwise pending. Because the figures are recomputed from the ledger of successful transactions rather than incremented blindly, the PO always reflects the true sum of what has actually settled.

Repeat callbacks are a fact of life with payment providers, so posting is idempotent. Each transaction carries a unique combination of provider and external id (the Daraja transaction id or the Paystack transfer code), which means a provider that retries the same confirmation cannot create a second payment or double-reduce the balance — the duplicate is recognised and ignored. This is the same discipline that protects sales reconciliation, applied to outgoing money: a confirmation is allowed to arrive more than once, but it can only ever count once.

The subtlest rule concerns timeouts, and getting it wrong costs real money. A timeout is not a failure — it means Daraja could not give a definitive answer in time, and the payment may still complete. So AWRA treats a timed-out payment as still pending and surfaces it that way; it does not mark it failed, because doing so could nudge a cashier into paying the same vendor a second time when the first payment actually went through. The operational instruction to teach is blunt: if a payment times out, check the vendor payments dashboard for the final status before ever sending it again.

Key takeaways

  • A confirmed payment recomputes the PO's amount paid, balance due, and status (paid / partial / pending) from successful transactions.
  • Posting is idempotent via a unique (provider, external id) — a retried callback can never double-post or double-reduce the balance.
  • A timeout means still pending, not failed — AWRA never marks it failed, to avoid nudging a cashier into paying twice.
  • After a timeout, confirm the final status on the vendor payments dashboard before re-sending.
05
Lesson 5 of 5 Reading 8 min

Manual records, security, and going live

Not every vendor payment runs through a digital rail. Bank EFTs done in online banking, cheques, and cash all happen outside AWRA, and the manual method exists to keep the books complete. Recording one captures the amount, the method, a reference (a cheque number or bank reference), and the date, and creates a transaction marked successful immediately — there is no webhook to wait for — which posts onto the PO balance just like a digital payment. Idempotency here is advisory: because the reference is user-supplied, AWRA warns on a duplicate rather than hard-blocking, trusting the person who saw the money move.

Security is layered and tenant-strict throughout. Every provider callback route is exempted from CSRF and can be locked to the provider's source IP ranges (Safaricom for Daraja, Paystack for transfers), so only the real provider can reach them. Paystack webhooks are HMAC-verified against the raw body using the tenant's own secret key before anything is acted on, and the M-Pesa security credential is rebuilt fresh per call rather than stored. Above all, credentials are never crossed between tenants: AWRA resolves the tenant first, loads that tenant's config, and uses only those keys — so one business can never pay out on another's account.

Going live is mostly credentials and registration, mirroring sales but for the outbound direction. M-Pesa payouts need the tenant's real initiator name and password plus Safaricom's public certificate on the server, and the B2C/B2B result and timeout URLs registered and IP-allow-listed. Paystack transfers need the tenant's secret key and the vendor transfer webhook URL added in the Paystack dashboard, with enough wallet balance to cover payments. With those in place the four methods sit behind one Pay Vendor action on the approved PO, and every payout — digital or manual — lands in the same vendor payments dashboard with its status, reference, and link back to the purchase order it settled.

Key takeaways

  • Manual records (cash/cheque/EFT) post to the PO immediately with no webhook; duplicate references warn rather than hard-block.
  • Callback routes are CSRF-exempt and IP-allow-listable; Paystack webhooks are HMAC-verified with the tenant's own secret key.
  • Credentials are never crossed between tenants — AWRA resolves the tenant, then uses only that tenant's config.
  • Going live = real initiator creds + certificate + registered B2C/B2B URLs for M-Pesa, and the secret key + transfer webhook + wallet balance for Paystack.

Finished the material?

Take the 5-question assessment and earn your certificate — 75% to pass.

Take the assessment

Help Center

Need a quick answer while you read?

Run inventory, procurement, assets, sales, and field work with approved AWRA guidance for setup, migration, integrations, security, pricing, and support.

Search all approved AWRA public help articles.

Open Help Center