CAWL Installments and Pay Later on WooCommerce
The problem CAWL merchants run into
You already take payments through CAWL, the French payment solution running on the Worldline platform. A customer asks to pay in three instalments. You look for the option in the WooCommerce extension settings, and there is nothing there.
Currently, the official CAWL extension for WooCommerce handles standard card payments. Not split payments, not deferred payments.
That leaves two poor options. Give up, and watch high-value carts walk away. Or bolt on a second provider such as Alma or Klarna, which means another contract, another commission, another reconciliation, and a checkout displaying two competing financing logos.
There is a third option, and it is the point of this article: the CAWL API can already do everything you need. It simply is not exposed in the extension.
What the API actually offers
The underlying platform provides four building blocks, which are enough to assemble a complete instalment payment:
- Hosted Checkout: a hosted payment session for the initial charge, with strong customer authentication handled by the bank
- Card-On-File: the initial payment is flagged as reusable for later payments
- SubsequentPayment: replay a payment from the initial payment identifier, typed as
installmentorrecurring - Pre-authorisation and deferred capture: the technical basis for Pay Later
Requests are authenticated with the platform’s signature scheme:
Authorization: GCS v1HMAC:{apiKeyId}:{base64(hmac-sha256(secret, stringToSign))}
The stringToSign concatenates the HTTP method, content type, date and resource path. One character off, one header in the wrong order, and you get a 401 with no further explanation. It is the first obstacle, and the most trivial.
The architecture: one CIT, then MITs
This is the crux, and the part that rightly worries every merchant: where are the cards stored?
Nowhere on your site, and that is exactly the point of this design.
- CIT, Customer Initiated Transaction. The customer pays the first instalment in the bank’s hosted flow, with strong authentication. The payment is flagged as reusable.
- MIT, Merchant Initiated Transaction. Later instalments replay that initial payment by referencing its identifier. The customer is not present, there is no new authentication, and you have never handled a card number.
This is the mechanism strong-authentication rules provide for this precise use case. Your site stores a transaction identifier, not banking data.
Splitting the schedule without losing a cent
A total of 1000 € in 3 instalments gives 333.33 €, three times, and one cent goes missing. Multiply that by your order volume and your books stop balancing.
Work in integer cents and put the remainder on the first instalment:
function split_amount( int $total_cents, int $count ): array {
$base = intdiv( $total_cents, $count );
$amounts = array_fill( 0, $count, $base );
// Remainder goes on the first instalment, the one actually charged up front.
$amounts[0] += $total_cents - ( $base * $count );
return $amounts;
}
The remainder on the first rather than the last: it is the only one charged with the customer present, so the only one whose amount is visible and accepted at purchase time.
Charging instalments with Action Scheduler
Do not use WP-Cron for bank charges. On a low-traffic site a job can fire hours late, or not at all.
Action Scheduler, already bundled with WooCommerce, gives you a persistent queue stored in the database, with an execution log:
as_schedule_single_action(
strtotime( $date . ' 09:00:00' ),
'my_process_installment',
array( 'order_id' => $order->get_id(), 'k' => $k, 'attempt' => 0 ),
'my-plugin'
);
Plan retries from day one. Expired cards and insufficient funds happen on every store. Two attempts two days apart cover the large majority of cases, after which a human should take over rather than the system keep pushing.
The webhook trap: idempotency
This is by far the most expensive mistake, because it never shows up in testing and always shows up in production.
The same payment event can reach you twice: through the bank’s notification and through the customer’s browser return. Or twice through the same channel, if the bank considers your first response failed and replays the call.
If your code simply reacts to the event, you confirm the order twice, you schedule two payment plans, and in the worst case you charge the customer twice.
The fix is three lines, provided you think of it:
if ( $order->get_meta( '_installment_' . $k . '_paid' ) ) {
return; // Already handled, do nothing.
}
$order->update_meta_data( '_installment_' . $k . '_paid', $amount_cents );
$order->save();
Verify notification signatures too. The platform signs the raw request body with HMAC-SHA256 using a dedicated secret. A REST endpoint left open without signature verification is an open door to marking any order as paid.
One last detail that catches people out: the webhook key pair signs every notification URL on the account. Depending on your setup, you may not be able to register a second URL alongside the official extension’s. You then have to observe the notifications addressed to it and handle yours along the way.
Pay Later
Deferred payment relies on a different mechanism: a pre-authorisation at checkout, then a capture X days later.
The thing to watch is the validity window of the authorisation. It depends on the card network and the issuing bank, and is often counted in a handful of days rather than a month. Advertising “pay in 30 days” without checking what your contract allows is a good way to collect a run of declined captures.
Offer delays you have validated, not the ones that look good on the product page.
Block Checkout
WooCommerce has made Block Checkout the default. A payment gateway written only for the legacy checkout simply will not appear there.
Each payment method has to be registered with the blocks registry, along with its JavaScript counterpart:
add_action( 'woocommerce_blocks_payment_method_type_registration', function ( $registry ) {
$registry->register( new My_Blocks_Support( 'my_installments' ) );
} );
If you have not moved to blocks yet, my Block Checkout migration service covers that groundwork.
Showing instalments on the product page, where it matters
A common mistake is to reveal instalments only at the payment step. By then, the customer has already decided.
Split payment works before the add-to-cart. “1290 €” and “or 3 x 430 €” do not trigger the same decision, and the product page is where that comparison happens.
On variable products, recalculate the amount client-side on every variation change, otherwise you display the monthly figure of the wrong variant.
A regulatory point to keep an eye on
Short-term, interest-free split payments long sat outside consumer credit rules in France. The European framework is changing, and part of these offers is progressively coming within the scope of credit, with the disclosure and assessment duties that follow.
I am not a lawyer and this article is not legal advice. Before putting a split payment offer live, have your setup and your wording validated by your counsel and your acquiring bank. This is the kind of subject where the cost of checking is nothing compared to the cost of fixing.
In short
Building instalments on CAWL is achievable, and probably simpler than adding another provider to your checkout. The difficulty is not the API call, it is everything around it: cent-accurate schedules, the charge queue, retries, idempotency, signature verification, and placement at the right point in the journey.
None of these is insurmountable. Each of them, forgotten, turns into a wrong order or a customer charged twice.
Taking payments with CAWL and want to offer instalments or Pay Later on your WooCommerce store? Let’s talk.
Need a WooCommerce store audit?
I'll send you a personalized report with the top priorities to improve. Free, no strings attached.