PHPackages                             two-inc/magento2 - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Payment Processing](/categories/payments)
4. /
5. two-inc/magento2

ActiveMagento2-module[Payment Processing](/categories/payments)

two-inc/magento2
================

Two B2B BNPL payments extension for Magento

2.0.0(2d ago)06.9k↑90.5%2[1 PRs](https://github.com/two-inc/magento-plugin/pulls)1OSL-3.0PHPPHP &gt;=8.1CI passing

Since Jul 12Pushed 1w ago2 watchersCompare

[ Source](https://github.com/two-inc/magento-plugin)[ Packagist](https://packagist.org/packages/two-inc/magento2)[ RSS](/packages/two-inc-magento2/feed)WikiDiscussions staging Synced yesterday

READMEChangelog (10)Dependencies (3)Versions (69)Used By (1)

 [![](view/frontend/web/images/logo.svg)](view/frontend/web/images/logo.svg)

Two — Magento 2 Payment Plugin
==============================

[](#two--magento-2-payment-plugin)

B2B Buy Now, Pay Later for Magento 2.3.3+. This plugin integrates [Two](https://www.two.inc/) as a payment method, letting merchants offer invoice-based checkout with flexible net terms.

What it does
------------

[](#what-it-does)

**For merchants:**

- Instant credit checks on business customers
- B2B guest checkout (up to 36% conversion uplift)
- Flexible invoice terms from 14 to 90 days
- Automatic invoicing via the [PEPPOL](https://peppol.eu/) e-invoicing network
- Partial capture and refunds
- Instant payment on fulfilment — Two assumes the credit risk

**For buyers:**

- Frictionless checkout with no onboarding
- Flexible repayment terms
- PDF and electronic invoicing straight to their ERP

Installation
------------

[](#installation)

Install via Composer:

```
composer require two-inc/magento2
php bin/magento module:enable Two_Gateway
php bin/magento setup:upgrade
php bin/magento cache:flush
```

In production mode, also deploy static content:

```
php bin/magento setup:static-content:deploy
```

Then configure the plugin under **Stores &gt; Configuration &gt; Sales &gt; Payment Methods &gt; Two**.

### Post-install steps

[](#post-install-steps)

Run these immediately after `setup:upgrade` to refresh the DI graph and clear any stale cache types:

```
php bin/magento setup:di:compile
php bin/magento cache:flush
```

If admin Configuration is missing expected Two/brand sections, the cause is almost certainly one of:

- A plugin registered only under `etc/adminhtml/di.xml` or `etc/crontab/di.xml` instead of `etc/di.xml`. See AGENTS.md for the DI-scope rule.
- An FPM worker holding stale opcache. Restart PHP-FPM (`systemctl reload php-fpm` or `kill -USR2 `).
- A cache type (config / layout / full\_page) in stale state. `bin/magento cache:flush` is the canonical fix.

Development
-----------

[](#development)

The development environment runs Magento in Docker with the plugin bind-mounted, so file changes are reflected immediately.

### Prerequisites

[](#prerequisites)

- Docker
- Make
- A Two API key ([request sandbox access](https://www.two.inc/))

### Quick start

[](#quick-start)

```
# Create the Magento container and install the plugin
make install

# Configure with your API key
make configure TWO_API_KEY=

# Start / stop
make run
make stop
```

After install, Magento is available at  (admin: , credentials: `exampleuser` / `examplepassword123`).

To use a different port: `make install PORT=5678`.

By default, the plugin points at Two's staging environment for `@two.inc` gcloud accounts, or sandbox for everyone else. You can override the API and checkout URLs explicitly:

```
make install TWO_API_BASE_URL=http://localhost:8000 TWO_CHECKOUT_BASE_URL=http://localhost:3000
```

In production mode these are ignored — the URLs are derived from the `mode` setting in the admin panel (sandbox/staging/production).

Run `make help` to see all available targets.

### Local-dev perf — disabled modules and what breaks

[](#local-dev-perf--disabled-modules-and-what-breaks)

`make install` runs `module:disable` on a fixed list of modules that aren't needed for plugin development but add significant load to `setup:di:compile` (every module's DI is re-generated) and to the storefront's RequireJS dependency graph (every enabled module's JS gets pulled into the boot, even on pages that don't use it). Disabling them cuts `setup:di:compile` time and drops storefront button-enable latency from ~10s to under a second on the sample-data catalog.

Module(s)Why it's disabled in dev`Magento_AdminAdobeImsTwoFactorAuth`, `Magento_TwoFactorAuth`TOTP setup required on every admin login — friction for local dev`Magento_Analytics`, `Magento_AdminAnalytics`, `Magento_CatalogAnalytics`, `Magento_CustomerAnalytics`, `Magento_QuoteAnalytics`, `Magento_ReviewAnalytics`, `Magento_SalesAnalytics`, `Magento_WishlistAnalytics`, `Magento_GoogleAnalytics`, `Magento_GoogleOptimizer`JS/tracking hooks that fire on every storefront load`Magento_PageBuilder`, `Magento_PageBuilderAnalytics`, `Magento_CatalogPageBuilderAnalytics`, `Magento_CmsPageBuilderAnalytics`, `Magento_PageBuilderAdminAnalytics`, `Magento_AwsS3PageBuilder`Loads the full PageBuilder ContentTypes JS tree on **every** storefront page — biggest single contributor to client-side boot time`Magento_NewRelicReporting` is **not** disabled — `Magento_GraphQl` declares a hard dependency on it, and disabling it cascades through every GraphQL module. It stays quiet at runtime when un-licensed.

**Consequence:** PageBuilder-driven CMS content (banners, slides, promo blocks edited via the visual editor) **will not render** in a `make install` environment. If you're testing brand content that relies on PageBuilder blocks, re-enable them manually inside the container:

```
docker exec magento php bin/magento module:enable Magento_PageBuilder Magento_PageBuilderAnalytics Magento_CatalogPageBuilderAnalytics Magento_CmsPageBuilderAnalytics Magento_PageBuilderAdminAnalytics Magento_AwsS3PageBuilder
docker exec magento php bin/magento setup:upgrade
docker exec magento php bin/magento setup:di:compile
docker exec magento php bin/magento cache:flush
```

Install also runs:

- `config:set dev/js/merge_files=1`, `dev/js/minify_files=1`, `dev/css/merge_css_files=1` — flatten the inline RequireJS bootstrap into a single merged bundle in the HTML.
- `setup:static-content:deploy --area frontend --theme Magento/luma --no-html-minify -f --jobs 4 en_US` — pre-bake the Luma theme so RequireJS's ~hundreds of runtime XHRs hit plain file IO instead of falling through Magento's `pub/static.php` router (a full Magento bootstrap per asset). Without this, on the sample catalog the storefront's "Add to Cart" button-enable latency is ~10s; with it, ~1s warm.

### Brand overlays

[](#brand-overlays)

Brand-specific overlay packages live in their own Composer packages and are installed separately from this plugin, not through `make install`. See `docs/brand-overlay-guide.md` for how overlay modules are structured.

### Debugging

[](#debugging)

Xdebug is installed automatically by `make install` but is disabled by default. To start in debug mode:

```
make debug
```

This activates Xdebug (port 9003) and disables all Magento caches for hot reload — PHP changes, templates, layout XML, and config changes are picked up on the next request without manual cache flushing. The only exception is DI wiring changes (new classes, plugins, or preferences in `di.xml`), which still require `make compile`.

**Setting breakpoints in VSCode:**

1. Install the [PHP Debug](https://marketplace.visualstudio.com/items?itemName=xdebug.php-debug) extension
2. Press **F5** to start listening (uses the included `.vscode/launch.json`)
3. Click the gutter next to any line in the plugin code to set a breakpoint
4. Browse to the Magento store — every request will trigger the debugger automatically

The debugger will pause at your breakpoint with full access to variables, call stack, and step-through execution.

### HTTPS proxy

[](#https-proxy)

For testing integrations that require HTTPS callbacks (e.g. the Two checkout flow), you can expose your local instance via an [FRP](https://github.com/fatedier/frp) reverse proxy.

**Setup (one-time):** install the FRP client (`frpc`):

- macOS: `brew install frpc`
- Linux: download from [GitHub releases](https://github.com/fatedier/frp/releases) and place `frpc` on your PATH

**Authentication:**

The proxy needs an `FRP_AUTH_TOKEN` to connect to the FRP server. The `start-proxy.sh` script resolves the token in this order:

1. **Command-line argument:** `./start-proxy.sh `
2. **Environment variable:** `export FRP_AUTH_TOKEN=` (or set it in `.env.local`)
3. **GCP Secret Manager:** falls back to `gcloud secrets versions access latest --secret=FRP_AUTH_TOKEN --project=two-beta`

Edit `frpc.toml` to point at your FRP server, then provide the token via any of the methods above.

**Usage:**

```
# Proxy starts automatically with make run / make debug.
# To run the proxy standalone in the foreground:
make proxy
```

### Tests

[](#tests)

```
# Unit tests
make test

# End-to-end API tests (requires a valid API key)
make test-e2e TWO_API_KEY=
```

### Other useful targets

[](#other-useful-targets)

TargetDescription`make compile`Recompile Magento DI (after adding/changing PHP classes, plugins, or preferences)`make logs`Tail the Two plugin debug and error logs`make format`Run Prettier on frontend JS/CSS/templates`make clean`Stop and remove the Magento containerReleases
--------

[](#releases)

Releases are cut automatically once CI passes on `main`.

### Tagging (automatic, gated on CI)

[](#tagging-automatic-gated-on-ci)

`.github/workflows/release.yml` is triggered by the `CI` workflow completing on `main`. When CI's conclusion is `success`, it:

1. Skips itself if the head commit is already a `chore: Bump version` commit, or if the SHA already carries a numeric tag.
2. Reads conventional-commit types in `..HEAD` to pick the bump level:

    - `BREAKING CHANGE:` / `!:` → **major**
    - `feat:` → **minor**
    - everything else → **patch**

    Linear ticket prefixes are supported (e.g. `INF-123/feat:`).
3. Runs `bumpver update -- --no-tag-commit --no-push` to rewrite `composer.json`, `etc/config.xml`, and `bumpver.toml`.
4. Tags `X.Y.Z` (bare numeric, matching the established tag convention), pushes the bump commit and tag under the org GitHub App identity, and creates a GitHub Release with a bucketed changelog (Breaking / Features / Fixes / Internals / Other) — so reading the Release page reveals at a glance why the bump was a major / minor / patch.

`.github/workflows/merge-back.yml` keeps `develop` fast-forwarded to match `main` after each release. `.github/workflows/auto-pr.yml` keeps a rolling sync PR open from `develop` to `main` with a preview of the next release notes — the same bucketing the actual Release page uses.

To trigger a release, merge the rolling sync PR into `main`. CI runs on the merged commit; once green, `release.yml` fires.

Links
-----

[](#links)

- [Two developer documentation](https://docs.two.inc/)
- [Magento plugin setup guide](https://docs.two.inc/developer-portal/plugins/magento)

License
-------

[](#license)

OSL-3.0 / AFL-3.0. See [composer.json](composer.json) for details.

###  Health Score

56

—

FairBetter than 97% of packages

Maintenance99

Actively maintained with recent releases

Popularity25

Limited adoption so far

Community19

Small or concentrated contributor base

Maturity71

Established project with proven stability

 Bus Factor2

2 contributors hold 50%+ of commits

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~24 days

Recently: every ~39 days

Total

60

Last Release

2d ago

Major Versions

1.16.2 → 2.0.02026-07-01

PHP version history (2 changes)1.16.0PHP &gt;=7.3

2.0.0PHP &gt;=8.1

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/40845434?v=4)[Sean Chen](/maintainers/two)[@two](https://github.com/two)

---

Top Contributors

[![brtkwr](https://avatars.githubusercontent.com/u/2181426?v=4)](https://github.com/brtkwr "brtkwr (320 commits)")[![dgjlindsay](https://avatars.githubusercontent.com/u/36418630?v=4)](https://github.com/dgjlindsay "dgjlindsay (313 commits)")[![Marvin-Magmodules](https://avatars.githubusercontent.com/u/20815641?v=4)](https://github.com/Marvin-Magmodules "Marvin-Magmodules (40 commits)")[![SalmanTwo](https://avatars.githubusercontent.com/u/119856921?v=4)](https://github.com/SalmanTwo "SalmanTwo (21 commits)")[![andersdovrantwo](https://avatars.githubusercontent.com/u/94359519?v=4)](https://github.com/andersdovrantwo "andersdovrantwo (3 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (3 commits)")

---

Tags

b2b-paymentsmagentoplugin

### Embed Badge

![Health badge](/badges/two-inc-magento2/health.svg)

```
[![Health](https://phpackages.com/badges/two-inc-magento2/health.svg)](https://phpackages.com/packages/two-inc-magento2)
```

###  Alternatives

[adyen/module-payment

Official Magento2 Plugin to connect to Payment Service Provider Adyen.

1673.2M10](/packages/adyen-module-payment)[checkoutcom/magento2

Checkout.com Payment Gateway for Magento 2

34276.3k1](/packages/checkoutcom-magento2)[mollie/magento2-hyva-checkout

Hyvä Themes Checkout module for Mollie Payments

11240.0k](/packages/mollie-magento2-hyva-checkout)[vipps/module-payment

Vipps MobilePay Payment Module for Magento 2

1098.4k](/packages/vipps-module-payment)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
