WP Manifestindependent plugin directory
manifest / ecommerce / osen-wc-mpesa

Osen WC Mpesa WP Registry grade F. Critical findings · 6 findingsWP RegistryCritical findings · 6 findingsOpen the reportF

WordPress Plugin that extends WooCommerce functionality to integrate MPESA for making payments, remittances, checking account balance, transaction status and reversals.

★ 101stars
v1.20.99latest · 6 years ago

Install

Download source zip No release zip yet

This is the repository archive. It installs, but the folder name will carry the branch suffix and updates will not flow.

Install straight from GitHub with WP-CLI:

wp plugin install https://github.com/osenco/osen-wc-mpesa/archive/refs/heads/master.zip
No built-in updater. New versions won't appear under Dashboard → Updates. Install new releases yourself, or let Git Updater track the repository.
       _

| | _ __ | _/ | | | | \ | | / | / \ | | | | | | | | | | | | | | | | | | | | | | | | | | | /\ / \ | | || | | | | | | || | | | | | | | |/ \ / | | __/ | | | | | | | | | | | | |\ \/ /| | | | | | | | | | | | | | | | | | \ / | | | | |__ __| | | | | | || || || | _/ | || |__| |/ || || | | | | |___|

M-Pesa For WooCommerce

WordPress Plugin that extends WooCommerce functionality to integrate M-Pesa for making payments, remittances, checking account balance transaction status and reversals.

M-Pesa Config Futher config

Installation

Getting started with M-Pesa for WooCommerce is very easy. All configuration is done in the WooCommerce settings in the WordPress admin dashboard.

Requirements

Your site/app MUST be running over https for the M-Pesa Instant Payment Notification (IPN) to work.

Auto-installation

Manual Installation

  • First, you need to download the latest release of the plugin from here.
  • Using an FTP program, or your hosting control panel, upload the plugin folder (wc-mpesa) to your WordPress installation’s wp-content/plugins/ directory.
  • Activate the plugin from the Plugins menu within the WordPress admin

That is all. You are now ready to receive and send money using M-Pesa on your WordPress and WooCommerce powered site.

Contributing

  • Fork the repo, do your magic and make a pull request.

Integration Cost

  • Our team of developers are on hand to provide assistance for when you wish to move from Sandbox(test) to Live(production) environment. This assistance is charged a flat fee of KSH 4000/$40

Acknowledgements

  • M-Pesa and the M-Pesa Logo are registered trademarks of Safaricom Ltd
  • WordPress and the WordPress logo are registered trademarks of Automattic Inc.
  • WooCommerce and the WooCommerce logo are registered trademarks of Automattic Inc.

License

This plugin is licensed under the GPLv3 license.

Releases

22 releases.

Tag
Published
Oct 12, 2021 5 years ago
Sep 27, 2021 5 years ago
v1.20.99 latest
Sep 8, 2020 6 years ago
Jul 23, 2020 6 years ago
Jul 18, 2020 6 years ago
Jul 17, 2020 6 years ago
Jun 18, 2020 6 years ago
Jun 11, 2020 6 years ago
Jun 4, 2020 6 years ago
Jun 4, 2020 6 years ago
May 27, 2020 6 years ago
May 24, 2020 6 years ago
May 24, 2020 6 years ago
Mar 31, 2020 6 years ago
Aug 6, 2019 7 years ago
Aug 8, 2018 8 years ago
Jul 13, 2018 8 years ago
Jul 7, 2018 8 years ago
May 27, 2018 8 years ago
May 8, 2018 8 years ago
May 2, 2018 8 years ago
Mar 14, 2018 8 years ago

These releases are tags only. The author does not attach a packaged zip, so there are no download counts to report.

F grade

Security

Osen WC Mpesa 1.20.99 · audited by WP Registry

Critical findings.

3 critical 2 high 1 medium
Audited release
1.20.99
Findings
6
Worst severity
critical
Content hash
9435874851c65a1672e0f81e…

Findings

  • critical Unauthenticated M-Pesa IPN completes any matching WooCommerce order

    auth_bypass

    wc_mpesa_process_ipn on template_redirect handles get_query_var('lipwa') with Access-Control-Allow-Origin: * and no capability, nonce, HMAC, or Daraja IP check. Checkout process_payment inserts an mpesaipn row with _reference = $order_id and _paid = 0. POST /lipwa/confirm (or ?lipwa=confirm) JSON-decodes php://input, looks up that row by BillRefNumber, then computes $ipn_balance = round(_paid)+round(TransAmount)-_amount. Result 0 marks the order completed/processing (woocommerce_mpesa_settings['completion']). Result < 0 is labeled 'overpayed' but paid-due < 0 is actually underpay, so TransAmount 0 (or omitted on PHP 7) completes the order. Attacker enumerates sequential order ids, POSTs {"BillRefNumber":"123","TransAmount":0,"TransID":"x","MSISDN":"2547...","FirstName":"x","MiddleName":"","LastName":"","TransTime":""}, and WC_Order::update_status fires with full-payment notes and wp_mail. /lipwa/reconcile does the same via CallbackMetadata if MerchantRequestID matches _request_id. /lipwa/timeout sets the order pending. Safaricom is never verified; any client can forge the callback.

    inc/functions.php:224-340,366-436,479-504

    Recommendation

    Reject callbacks that do not authenticate as Daraja (shared secret/signature query param, mTLS, or allowlist). Bind confirm to the pending mpesaipn row, require TransAmount to equal the order total (fix the inverted overpay check), ignore attacker-supplied order status, and refuse to complete orders from unauthenticated clients. Prefer wc-api + wp_safe_remote_* verification against Daraja Transaction Status rather than trusting the POST body.

  • critical Unauthenticated SQL injection via IPN php://input in postmeta lookup

    sql_injection

    wc_mpesa_post_id_by_meta_key_and_value interpolates $key and $value directly into $wpdb->get_results with no $wpdb->prepare/esc_sql. Confirm passes BillRefNumber from json_decode(php://input); reconcile and timeout pass MerchantRequestID from the same stream. php://input is not processed by wp_magic_quotes(), so a JSON quote in those fields closes the SQL string. Unauthenticated POST ?lipwa=confirm with BillRefNumber=x' UNION SELECT ... -- extracts wp_postmeta/wp_options (including woocommerce_mpesa_settings Daraja secrets) and wp_users hashes. GET ?pesaipn&order= uses $_GET (magic-quoted) and is not this finding.

    inc/functions.php:13-26,272,377,491

    Recommendation

    Replace the helper with $wpdb->prepare('SELECT post_id FROM ... WHERE meta_key = %s AND meta_value = %s', $key, $value) (or a WP_Query/meta_query). Do not concatenate php://input into SQL. Keep the IPN unauthenticated-surface as small as possible.

  • critical Unauthenticated stored XSS: IPN-written _receipt is injected on the thank-you page via jQuery.html/append

    xss_stored

    lipwa/confirm stores TransID as mpesaipn _receipt (and FirstName/MiddleName/LastName as _customer); reconcile stores CallbackMetadata receipt the same way. The woocommerce_thankyou_mpesa callback prints a 3s poller that $.get(?pesaipn&order=ID) and concatenates data.receipt into jQuery .append() on .woocommerce-order-overview and into $('#mpesa_receipt').html(). jQuery html/append is a script-evaluating consumer (script tags and event-handler markup). An attacker who forges confirm for a victim order_id plants <img src=x onerror=...> or a skimmer snippet; the shopper's thank-you page executes it while 'Confirming receipt' runs. The same unescaped _customer/_receipt values are echoed in the mpesaipn admin list (src/Post/Types/C2B.php), so an administrator opening Payments gets stored XSS and session takeover. Write path is unauthenticated; read path is the customer thank-you page (order-received) and edit_pages users.

    inc/functions.php:144-208,331-337,418-424,510-520

    Recommendation

    Treat _receipt as untrusted. Return it JSON-encoded and insert with textContent/jQuery.text, never html/append of concatenated markup. Sanitize/escape on write (ctype/alnum M-Pesa receipts) and esc_html on the admin list. Do not accept confirm/reconcile bodies from anonymous clients (see IPN finding).

  • high Unauthenticated STK Push resend for any order id

    missing_auth

    lipwa=request reads $_POST['order'], loads that WC_Order, and calls STK::request($phone, $total, $order_id, ...) with the site's Daraja consumer key/secret/passkey. No nonce, capability, order-key, or CSRF check. Anyone who can guess a sequential order id triggers a live Lipa na M-Pesa PIN prompt on the billing phone, burns the merchant's Daraja quota, and can harass buyers. The thank-you 'Resend STK Push' form posts to this same public URL (home_url('lipwa/request')) by design for the buyer, but the handler never proves the caller owns the order.

    inc/functions.php:236-247

    Recommendation

    Require a WooCommerce order key (or a shopper nonce created on the thank-you page) and refuse to STK Push orders the caller does not own. Rate-limit per order. Do not expose this as a bare query_var on the front end.

  • high Unauthenticated disclosure of M-Pesa receipts and raw IPN bodies

    information_disclosure

    GET ?pesaipn&order=<id> (no auth) returns JSON {receipt: get_post_meta(_receipt)} for any checkout-created mpesaipn row — M-Pesa transaction ids for other customers. Independently, CPT mpesaipn is registered public=false but publicly_queryable=true, show_in_rest=true, capability_type=page, and process_payment/confirm publish posts whose post_content is the Daraja JSON or php://input (phones, amounts, receipts, MerchantRequestID). /wp-json/wp/v2/mpesaipn lists published payments; visiting the publicly_queryable permalink dumps post_content. Sandbox (default env) also supports('editor'), so REST includes content. Combined with the unauthenticated IPN write path, attackers can both plant and read payment records.

    inc/functions.php:510-521

    Recommendation

    Drop pesaipn or gate it with the order-received key / session. Set mpesaipn publicly_queryable=false, show_in_rest=false, public=false. Do not store raw callbacks in published post_content; use private meta.

  • medium Unauthenticated C2B URL registration using the merchant Daraja app

    missing_auth

    lipwa=register calls C2B::register() with the site's consumer key/secret and shortcode, posting ConfirmationURL/ValidationURL to Safaricom. The switch default for any other lipwa value is exit(wp_send_json(C2B::register())), so GET /lipwa/foo both fires the live registerurl API and returns Daraja's JSON to the client. No capability check (the WooCommerce settings page links this as a one-click admin action). An unauthenticated visitor can repeatedly re-register URLs, exhaust Daraja, and inspect success/error bodies. Redirect after register uses wp_redirect(wp_get_referer()) rather than wp_safe_redirect.

    inc/functions.php:342-364,505-507

    Recommendation

    Require manage_woocommerce and a nonce. Never register URLs in the IPN default branch. Use wp_safe_redirect to admin settings. Return no Daraja payload to anonymous clients.

WP Registry hashes the installable build and reports on that exact bytes-for-bytes copy. Embargoed findings are withheld until they are disclosed, so a clean verdict means nothing public is outstanding. WP Manifest does not audit code itself.