# Ultraner with WooCommerce

This is the one entry here that is a real integration rather than a client library: a gateway plugin that appears at checkout without you writing code.

- Language: PHP
- Client: plugin (https://github.com/Ultraner/ultraner-wordpress)
- Human version of this page: https://ultraner.com/frameworks/woocommerce
- Live in 14 markets: Benin, Cameroon, DR Congo, Gabon, Ivory Coast, Kenya, Mozambique, Republic of Congo, Rwanda, Senegal, Sierra Leone, Tanzania, Uganda and Zambia

## The mistake this framework invites

Set the webhook URL in the plugin settings, not in your Ultraner dashboard alone. WooCommerce marks the order paid from the webhook, so a missing URL leaves paid orders sitting on hold.

## Install

```bash
Upload the plugin from sdks/ultraner-wordpress, then activate it
```

## Charge a mobile money wallet

```
// No code. The plugin registers Ultraner as a WooCommerce payment gateway.
//
//   WooCommerce -> Settings -> Payments -> Ultraner
//     API key          uk_live_... (or uk_test_... to try it first)
//     Webhook secret   from the same dashboard page
//     Webhook URL      https://your-shop.example.com/?wc-api=ultraner
//
// To change which networks appear at checkout, filter them:

add_filter('ultraner_enabled_providers', function ($providers) {
    return ['Vodacom', 'Tigo'];   // provider codes, not brand names
});
```

## Verify a webhook

Where framework differences actually bite: the signature is computed over
the raw body, so any middleware that parses JSON before you read it will
break verification.

```
// The plugin verifies and handles the webhook for you. To react to it:

add_action('ultraner_payment_completed', function ($order_id, $event) {
    $order = wc_get_order($order_id);
    // The plugin has already marked it paid. This is for your own work.
    $order->add_order_note('Paid via ' . $event['provider']);
}, 10, 2);

// Orders stuck on hold almost always mean the webhook URL is unset or
// unreachable, not that the payment failed.
```

## Worth knowing

The plugin is in the repository and is not yet published to the WordPress plugin directory, so installation is by upload.

## Things that are true regardless of framework

- **Amounts are whole units in most African currencies.** 5000 TZS is five
  thousand shillings, not fifty. Read `decimals` from
  https://ultraner.com/ai/context/currencies.json rather than assuming cents.
- **A charge is asynchronous.** A 200 means the prompt reached the payer's
  handset, not that they paid. Poll
  `GET /v1/payments/express/status/{transaction_id}` or wait for the webhook.
- **A charge takes `provider`; a payout takes `network`.** Both take
  `account_number`, never `accountNumber`. Unknown keys are stripped
  rather than rejected, so a wrong name does not error: the recipient
  disappears and the call fails as a missing required field.
- **Paying money out needs `X-Signature-Key` and `Idempotency-Key`.**

## More

- Every framework: https://ultraner.com/frameworks
- API reference: https://ultraner.com/docs
- The whole reference as Markdown: https://ultraner.com/docs.md
- OpenAPI: https://ultraner.com/openapi.json
- Agent skills: https://github.com/Ultraner/ultraner-skills
- Machine-readable index of all of this: https://ultraner.com/ai/context/frameworks.json
