Skip to content

0.5.0

Choose a tag to compare

@AlexanderBuzz AlexanderBuzz released this 15 Sep 07:26
· 45 commits to master since this release
98582c9

The core half of the payment-status contract (LedgerDirect-Zahlungsstatus-Vertrag.md): one answer shape for "is this order paid?" that every adapter serves, plus the wrong-asset rule it derives from. Purely additive — no port, signature or schema change, nothing an adapter has to do before raising its constraint to ^0.5.

New: PaymentStatus (#5)

Three plugins answered the same question in three formats — PrestaShop {status, redirect, seconds_left}, Shopware and Magento {success: bool}, WooCommerce no route at all — and none of them could report a partial payment or a wrong token, even though the freshly rendered payment page already shows both. A page after a poll knew less than the same page after a reload.

Core\Payment\PaymentStatus is derived, never stored: a pure function of PaymentIntent, SettlementPolicy and the clock. Five states, each of which a payment page has to be able to show:

State Meaning Extra fields
waiting Nothing arrived, quote still valid seconds_left
partial Arrived in the quoted asset, does not settle amount_paid, shortfall
wrong_asset Arrived, but another currency or another issuer amount_paid, shortfall
settled Paid — the only terminal state amount_paid
expired Nothing arrived, quote expired —

The derivation order is part of the contract: settled → expired → waiting → wrong_asset → partial. A partial payment on an expired quote is therefore partial, not expired — the customer sent real money, and the page has to say so before it talks about validity periods.

toArray() carries exactly schema_version, state, base_asset, amount_requested, amount_paid, shortfall, seconds_left, always all seven. shortfall takes the shape of amount_requested, with the quoted currency and issuer, so an adapter formats it through the same code path as the request. amount_paid is what actually arrived, so a wrong token can be named. redirect and amount formatting stay in the adapter; the core knows no HTTP.

PaymentStatus::MIN_SYNC_INTERVAL_SECONDS (5 s) names the throttle every status endpoint must apply before syncing the same order with the ledger again. The throttling itself and the last-sync timestamp live in the adapter — the core only keeps four platforms from picking four numbers.

New: SettlementPolicy::isWrongAsset() and creditedValue() (#4)

Four adapters, two derivations of the same rule: Magento and PrestaShop inferred "wrong token" from shortfall() === amountRequestedValue(), WooCommerce and Shopware compared issuer and currency themselves. Both are correct today, which is exactly what makes it worth fixing now — the same shape of duplication is how a hard-coded "RLUSD" survived in Shopware's USDC branch.

isWrongAsset() states the rule once; isSettled() and the shortfall calculation both ask it. creditedValue() is the number a payment page means by "received so far", and it credits "0" for a payment in the wrong asset however large it was. Credited plus outstanding is always exactly the requested amount. Adapters must not re-derive either.

What adapters do with this

Raise the constraint to ^0.5, then build the status endpoint on PaymentStatus::fromIntent()->toArray() plus redirect. Order per the handover: PrestaShop as the reference, then Shopware (drop XrplPaymentController::isWrongToken() and _loginRequired on the check route), then WooCommerce, then Magento. Authentication is a per-order secret, not a login — guest checkout is the rule.