PovitoDevelopers

Povito Checkout for WooCommerce#

The plugin turns Povito Checkout into a WooCommerce payment method: ZainCash, FIB, Qi Card, FastPay, cards and cash on delivery, on Povito's hosted page, with the shopper returned to your store.

Requires WordPress 6.3+, WooCommerce 8.0+, PHP 8.1+. Works with the classic shortcode checkout and the Cart/Checkout blocks, and with High-Performance Order Storage. It is built on the PHP SDK, which it bundles.

How it confirms a payment#

  1. The shopper picks Povito Checkout and places the order.
  2. The plugin creates a checkout session and redirects them to the hosted page.
  3. They pay.
  4. Povito sends a signed webhook, and the shopper lands back on your order-received page.
  5. Either way the plugin reads the session back from the API, and only then marks the order paid.

Neither the webhook nor the return URL marks an order paid

A webhook is a prompt to GET /checkout/sessions/{id}. Landing on the return URL proves only that a browser followed a redirect. The plugin marks an order paid on payment_status = paid and a captured amount and currency equal to the order's; anything else puts the order on hold with a note.

If the webhook never arrives — a firewall in front of the site, a queue backing up — the thank-you page reads the session itself, and a scheduled job reads it again at 5 minutes, 30 minutes and 2 hours. An order is not lost because a delivery was.

Install#

Upload the zip at Plugins → Add New → Upload Plugin, then activate.

Building from the source in the Povito-Checkout monorepo instead:

Build a zipbash
cd plugins/woocommerce/povito-checkout
./bin/build-zip.sh          # → build/povito-checkout-0.1.0.zip

The script runs composer install --no-dev --optimize-autoloader first — that step is what puts the SDK and its autoloader into the package. Without it the plugin refuses to load and says so in an admin notice.

Configure#

WooCommerce → Settings → Payments → Povito Checkout.

Setting What it is for
Enable/Disable Offer the gateway at checkout
Test mode Use the test key and the simulated gateway
Title / Description (English, Arabic, Kurdish) What the shopper reads. A language left empty falls back to English; the page follows the site's locale
Test / Live secret key povito_ck_test_… and povito_ck_live_…
Test / Live webhook secret whsec_…, shown once when you create the endpoint
Methods to offer Empty means every method enabled on your account. With a key configured, the list is fetched from GET /payment_methods, so you pick from what exists
Order status after payment Or leave it to WooCommerce
Debug log WooCommerce → Status → Logs, source povito-checkout

Keys are per mode. The plugin uses the test pair when test mode is on and the live pair when it is off, and never mixes them — see keys and environments.

The webhook endpoint#

Register this URL in the Povito portal, once for test and once for live:

Your endpointtext
https://your-store.example/wp-json/povito-checkout/v1/webhook

The settings screen prints the exact URL for your site. Copy the signing secret the portal gives you into the matching field; it is shown once.

The route is open to the world by design: the Povito-Signature header is the authentication. It is verified against the raw request bytes with a constant-time compare and a five-minute tolerance before anything else happens, and an unsigned or stale request gets 401 without the body being read.

A blocked REST API is survivable

If your host blocks /wp-json/, or you are developing behind NAT, orders are still confirmed on the return page and by the scheduled check. The webhook makes confirmation instant, not possible.

Test with a povito_ck_test_ key#

  1. Turn Test mode on and paste the test secret key.
  2. Create a test webhook endpoint for the URL above; paste its secret into Test webhook secret.
  3. Buy something in your own shop. On the hosted page use the test phone +9647500000000 with OTP 000000, then press Succeed.
  4. The order should be paid before you finish reading the thank-you page, with two notes on it: the session created, and the payment confirmed.

Worth trying while you are there, because these are the paths that go wrong in production:

Do this Expect
Press Fail on the hosted page Order stays unpaid with a note; the shopper is told they can pay again
Press Cancel The same, from the other direction
Close the tab after paying The webhook marks the order paid without the browser
Put rubbish in the webhook secret and pay The delivery is rejected 401, and the thank-you page still confirms the order
Refund from the order screen A refund is created; cash on delivery says it cannot be refunded

An admin notice sits on your WooCommerce screens for as long as test mode is on, so nobody ships an order believing they were paid. More on the simulated gateway in testing.

Go live#

  1. Finish verification in the portal and issue a live secret key.
  2. Create a live webhook endpoint for the same URL; paste its secret into Live webhook secret.
  3. Add your store's domain to the merchant's allowed return hosts — an unregistered success_url is refused at create time with 422 return_host_not_allowed.
  4. Turn Test mode off.
  5. Buy something real and cheap, then refund it.

Money and line items#

Amounts are integers in the currency's minor unit, and IQD has no minor unit: 45,000 dinars is 45000. The plugin converts the order total with the SDK's Money helper, so a store in IQD and a store in USD both come out right.

The breakdown sent to the hosted page matches what WooCommerce showed the shopper: products at their pre-discount subtotal, then shipping, then fees, then the coupon as one discount line. The API requires that these reconcile to the total. Where per-line rounding leaves a dinar over — three items at 3,333.33 in a currency with no fractions — the remainder becomes an explicit Rounding line rather than a failed checkout. If the breakdown is off by more than rounding could explain, the plugin sends a single line for the order total instead of guessing.

Refunds#

Refund from the WooCommerce order screen as usual and the plugin calls the API.

  • Cash on delivery, and any rail without a refund API, answer 422 method_not_refundable — refund those by other means.
  • An accepted refund comes back requested or processing and settles asynchronously. WooCommerce records it straight away; the order gets a note when Povito confirms it.
  • You cannot refund more than was captured.

The rules are the same as everywhere else: refunds.

Order metadata#

Stored on every order the plugin touches, and shown on the order screen:

Key What
_povito_session_id The checkout session
_povito_attempt_id The payment attempt that succeeded
_povito_method / _povito_gateway How they paid
_povito_gateway_reference The vendor's own reference; also the WooCommerce transaction id
_povito_captured_amount Minor units, the ceiling for refunds
_povito_mode live or test

Uninstalling removes the settings, the caches and the scheduled job. It leaves this metadata alone: it is the record of how an order was paid, and a shop that loses it cannot answer a chargeback.

Hooks#

Hook Use
povito_checkout_session_params (filter) Last word on the body sent to the API
povito_checkout_api_base_url (filter) Point at another environment
povito_checkout_api_timeout (filter) Seconds per attempt, default 15
povito_checkout_icon (filter) The icon shown at checkout
Add your own metadataphp
add_filter( 'povito_checkout_session_params', function ( array $params, WC_Order $order ): array {
	$params['metadata']['crm_id'] = (string) $order->get_meta( '_crm_id' );

	return $params;
}, 10, 2 );

Languages#

Text domain povito-checkout, with Arabic (ar) and Kurdish Sorani (ckb) catalogues. WordPress calls Sorani ckb; the plugin routes both ckb and ku to Povito's Kurdish hosted page. The gateway's own title and description are settings rather than translations, because that wording is the merchant's.

Troubleshooting#

Symptom Cause
The gateway is missing at checkout No secret key for the current mode. The plugin hides itself rather than failing at the last click; an admin notice says so
"Povito Checkout is missing its dependencies" The plugin was copied without vendor/. Run composer install --no-dev, or install the packaged zip
Orders confirm only on the thank-you page The webhook is not reaching the site, or its secret is wrong for this mode. Deliveries and their status codes are listed in the portal
422 return_host_not_allowed in the log The store's domain is not on the merchant's allowed return hosts
An order went on hold with an amount note The captured amount did not equal the order total. Nothing is marked paid until a human looks

Turn on Debug log and read WooCommerce → Status → Logs, source povito-checkout. Keys and secrets are masked there, and request and response bodies are never logged — they carry the shopper's phone number and address. The error codes are in errors.