# Billie for WooCommerce

A WooCommerce payment gateway that integrates [Billie's](https://www.billie.io/) B2B Buy Now, Pay Later solution. Business customers can pay by invoice on flexible terms; merchants get paid upfront with full fraud and credit-risk protection.

## Requirements

| Requirement | Minimum |
|---|---|
| WordPress | 4.6 |
| WooCommerce | 3.0 |
| PHP | 7.4 |

Tested up to WordPress 7.0.4 and WooCommerce 11.0.1.

> **Note:** WooCommerce 11 itself requires WordPress 6.9 or newer, so the WordPress 4.6 minimum
> above only applies to older WooCommerce releases in practice.

## Features

- **Flexible payment terms** — configurable invoice due dates from 7 to 120 days
- **Real-time order approvals** — Billie's risk engine approves orders instantly at checkout
- **Full risk coverage** — fraud and credit default risk transferred to Billie on authorization
- **WooCommerce Blocks support** — works with the modern block-based cart and checkout
- **HPOS compatible** — supports WooCommerce custom order tables
- **Refund support** — partial and full refunds processed via the Billie API
- **Flexible capture** — capture on "Processing" status or defer to "Completed"
- **Sandbox mode** — test your integration before going live

### Third-party plugin compatibility

- PDF Invoices & Packing Slips (invoice numbers)
- Germanized Pro (invoice numbers)
- German Market (invoice numbers)
- F4 Salutation Plugin
- Separate street / house-number billing fields

### Where the invoice number comes from

Capturing an order sends `POST /invoices` to Billie, and Billie requires the merchant's own
invoice number on that call. The plugin does **not** generate one. `PaymentInfo::get_invoice_id()`
tries four sources in order and aborts the capture if all are empty:

| Order | Source | Meta / call |
|---|---|---|
| 1 | PDF Invoices & Packing Slips | `_wcpdf_invoice_number` |
| 2 | Germanized **Pro** | `wc_gzdp_get_order_last_invoice()` |
| 3 | German Market | `_wp_wc_running_invoice_number` |
| 4 | Manual entry on the order screen | `_billie_invoice_id` |

Two of these look configured while supplying nothing, so the plugin reports which one applies
rather than only "Invoice ID not set" (`PaymentInfo::get_invoice_source_hint()`):

- **PDF Invoices ships with invoice documents disabled.** Verified against 5.15.2: out of the box
  `is_enabled()` is false, no `_wcpdf_invoice_number` is ever written, and the capture fails on a
  shop that appears correctly set up. Enable it under **WooCommerce → PDF Invoices → Documents →
  Invoice**.
- **Germanized invoicing is Pro-only.** The free edition has no invoice concept at all — not a
  different function prefix, no `wc_gzd_` counterpart to fall back to. A shop on the free edition
  will never produce a number. `PaymentInfo::germanized_edition()` distinguishes `pro` / `free` /
  none so the message can say so.

**One invoice number per order.** PDF Invoices reuses the same number when asked for a second
invoice on an order (verified: two `wcpdf_get_document( 'invoice', $order, true )` calls both
returned `00001`), and the number lives in a single meta key that is overwritten rather than
appended. This is why partial captures are not supported — each partial capture is a separate
Billie invoice needing its own number, and no supported source can supply the second one.
Germanized Pro is the one candidate that might, given `get_order_**last**_invoice` implies a list,
but that is unverified — it is commercial and was not available for testing.

## Installation

1. Download the plugin zip from [WordPress.org](https://wordpress.org/plugins/billie-for-woocommerce/) or install directly from the WooCommerce marketplace.
2. Upload and activate the plugin in **Plugins → Add New**.
3. Go to **WooCommerce → Settings → Payments → Billie** and enter your credentials.
4. [Register for a Billie merchant account](https://www.billie.io/onlineshop-registrierung) if you do not already have one.

## Configuration

Navigate to **WooCommerce → Settings → Payments → Billie** to configure:

| Setting | Description |
|---|---|
| Enable | Activate the payment method at checkout |
| Title | Label shown to customers at checkout |
| Payment term | Invoice due date in days (7–120) |
| Capture on | Trigger capture on *Processing* or defer to *Completed* |
| Environment | Switch between Sandbox and Production |
| Client ID | Your Billie API client ID |
| Client Secret | Your Billie API client secret (stored securely) |

## Development

### Stack

- PHP (WordPress/WooCommerce plugin API)
- JavaScript / React (WooCommerce Blocks integration), bundled via webpack
- Yarn for dependency management

### Setup

```bash
git clone https://github.com/ozean12/billie-woo-plugin.git
cd billie-woo-plugin
yarn install
yarn build          # production build → resources/build/
yarn dev            # watch mode
```

### Project structure

```
billie-for-woocommerce.php   # Plugin entry point
src/
  Billie/
    Admin/           # WP admin pages and option handlers
    Billie/          # Core gateway, API client, Blocks support
    Exceptions/      # Domain exceptions
    Plugin.php       # Bootstrap
client/              # JavaScript source (excluded from SVN)
resources/build/     # Compiled JS/CSS (included in SVN, excluded from git)
views/               # PHP view templates
lang/                # Translation files
```

### Releasing to WordPress.org

Releases are deployed automatically via GitHub Actions ([`.github/workflows/deploy.yml`](.github/workflows/deploy.yml)) to the [WordPress.org SVN repository](https://plugins.svn.wordpress.org/billie-for-woocommerce/).

To trigger a release:

```bash
git tag v2.3.0
git push origin v2.3.0
```

The workflow requires `SVN_USERNAME` and `SVN_PASSWORD` secrets set in the GitHub repository settings.

> **Note:** `client/` source files are excluded from the SVN release via `.distignore`. The compiled `resources/build/` output is included.

## Changelog

See [changelog.txt](changelog.txt) for the full history.

### 2.3.0 — 2026-08-14
- Security: Add `exit` after `wp_redirect()` to prevent continued execution after redirect
- Security: Remove OAuth access token from debug log to prevent credential exposure
- Security: Change `client_secret` admin field to `type=password` to protect credentials in UI
- Fix: Checkout logo 404 — the URL used the npm package name instead of the plugin slug
- Fix: Blocks checkout script registered without its declared dependencies
- Fix: Order screen meta boxes invisible and invoice number unsavable under HPOS
- Fix: Invoice number could not be entered on a completed order whose capture failed
- Fix: Empty invoice field discarded a stored invoice number on save
- Fix: Account owner on transfer details showed the shop name instead of Billie GmbH
- Fix: Declines reported as technical failures
- Fix: Classic checkout logo pointed at the wrong directory
- Add: Failed captures are flagged, surfaced on the order screen and retried automatically
- Add: Status changes that cannot capture are refused on the order screen
- Add: Unique merchant order reference in sandbox
- Add: Include plugin version in HPP checkout session create payload
- Update: Bump axios, webpack, and transitive dependencies to patched versions
- Update: Tested up to WordPress 7.0.4 and WooCommerce 11.0.1

### 2.2.0 — 2024-06-11
- Add: Moved all settings to the gateway options page
- Fix: Better interoperability with other plugins

## License

MIT — see [LICENSE](LICENSE) for details.
