The sketch below shows the logic your WordPress code needs, not a published plugin or SDK. Keep API credentials on the server only, verify every postback, and make the handler safe to run more than once for the same order. For a wider view of API work, read our M-Pesa API guide for Kenya.
Conceptual PHP: WooCommerce postback handler
// Conceptual only - not a published plugin or SDK
add_action('rest_api_init', function () {
register_rest_route('store/v1', '/paygate-postback', [
'methods' => 'POST',
'callback' => 'handle_paygate_postback',
'permission_callback' => '__return_true',
]);
});
function handle_paygate_postback(WP_REST_Request $request) {
// 1. Verify the postback using the method agreed at onboarding
if (!verify_postback($request)) {
return new WP_REST_Response(null, 401);
}
$data = $request->get_json_params();
$order = wc_get_order($data['merchant_reference']); // placeholder field
if (!$order) {
return new WP_REST_Response(null, 404);
}
// 2. Repeat postback for a paid order: acknowledge and stop
if ($order->is_paid()) {
return new WP_REST_Response(null, 200);
}
// 3. Check the KES amount before changing status
if ((float) $data['amount'] !== (float) $order->get_total()) {
$order->update_status('on-hold', 'Amount mismatch - review');
} elseif ($data['status'] === 'success') {
$order->payment_complete($data['transaction_id']);
} elseif ($data['status'] === 'failed') {
$order->update_status('failed', 'M-Pesa payment not completed');
}
return new WP_REST_Response(null, 200);
}
Conceptual example only. Field names, status values, verification method and endpoints are placeholders and will be confirmed with our team during onboarding. Test on a staging site before going live.