A Cybersource payment gateway for Drupal Commerce, now on drupal.org

I have published cybersource_sop: a Secure Acceptance Silent Order POST gateway for Drupal Commerce 3. The card data never touches your server, and every reply is signature-verified before a payment is recorded.

I have released a new contributed module on drupal.org: cybersource_sop, a Cybersource payment gateway for Drupal Commerce 3. It uses Cybersource’s Secure Acceptance Silent Order POST method, which means the customer types their card details into your checkout, the browser sends them straight to Cybersource, and your Drupal server never sees the card number. This is the write-up of what it does, how it is built, the security review it went through before it shipped, and where it fits if you run a Drupal shop on Cybersource.

Most Drupal Commerce sites that take card payments do so through a payment gateway module that talks to a third-party processor. The choice of gateway matters more than it looks. It decides where the card data flows, how much of the PCI DSS burden lands on you, and how much trust you are placing in code that sits directly in the payment path. Cybersource (Visa’s payment platform) is a common processor for businesses that have outgrown the simplest options, but the contributed Drupal support for it has been thin and not always to the standard a production payment path deserves. So I built one and published it.

Published module

Cybersource SOP for Drupal Commerce

Project
drupal.org/project/cybersource_sop
Install
composer require drupal/cybersource_sop
Method
Cybersource Secure Acceptance, Silent Order POST (SOP)
Requires
Drupal Commerce 3, Drupal 10.3+ or 11
Licence
GPL-2.0-or-later
Source
git.drupalcode.org/project/cybersource_sop

What Silent Order POST actually means

Cybersource Secure Acceptance comes in a few flavours, and the difference between them is precisely the difference in where the card number goes and therefore how exposed you are. Silent Order POST is the variant where the card fields are rendered on your own checkout page, styled like the rest of your site, but the form does not submit back to your server. It POSTs the card details directly to Cybersource over HTTPS. Cybersource processes the payment and sends a signed reply back to a return URL on your site, and the module verifies that signature before it believes a word of it.

The practical upshot is that the sensitive primary account number never lands on your infrastructure, is never written to your logs, and is never held in your database, even for a moment. You get a checkout that looks like part of your site rather than a redirect to someone else’s branded page, without your servers ever touching the card number. That is the appeal of SOP, and it is the whole reason to prefer it over a plain on-site form.

Where it sits on the PCI scale. Because the card fields are served from your page, an SOP integration falls under SAQ A-EP rather than the lighter SAQ A you would get from a fully hosted iframe. That is an honest trade: you accept a slightly larger compliance questionnaire in exchange for a checkout that is fully part of your own site and under your own styling. If your priority is the absolute minimum PCI scope, an iframe-based method is lighter; if you want the card data off your server and a native-looking checkout, SOP is the sweet spot, and this module is built for it.

How the module works

It registers as an off-site payment gateway in Drupal Commerce. At checkout, it builds the set of fields Cybersource expects, signs them with an HMAC derived from your secret key, and renders the form so the browser posts it to Cybersource. When Cybersource finishes, it calls back to a return route on your site with a signed reply. The module recomputes the signature over the returned fields and rejects anything that does not match, which is what lets that route be open to the world while still being trustworthy: the signature, not a session, is the authentication.

Only once the signature checks out does it interpret the result and record the payment against the order through Commerce’s normal payment API. It supports both authorisation only and sale (authorise and capture in one step). Managing a payment after the fact, capturing an authorisation, voids, refunds, is deliberately left to staff in the Cybersource Business Center rather than being driven from Drupal, which keeps the module’s surface small and keeps the most sensitive operations behind Cybersource’s own access controls.

Credentials live in a file, on purpose

One design decision is worth calling out because it is a security choice rather than a convenience one. The Cybersource credentials are never stored in Drupal configuration, so they are never written to the database and never exported into your config or your git repository. They are read at runtime from a fixed file in Drupal’s private filesystem, outside the web root, resolved per environment and currency: one test profile for everything in test mode, and one live profile per currency in live mode.

The path is hardcoded and intentionally not configurable. A configurable credential path sounds harmless until you notice that anyone with the “administer payment gateways” permission could then point the gateway at credentials they control. Removing the setting removes that entire class of attack. It is a small example of a recurring theme in this module: prefer the design that has fewer ways to go wrong, even when it is slightly less flexible.

The bar I held it to before publishing

Anyone can push code to drupal.org. The question is what state it is in when you do. Code that sits in the payment path of a live shop should be held higher than ordinary contrib, and I treated it that way.

The security review, and what it changed

Before release the gateway went through a focused security review, and that review found real problems. I think it is more useful to be specific about them than to wave at “it has been reviewed,” so here are the three that mattered, and what each fix does.

1. A signing quirk that could let two different values share one signature

The code that builds the HMAC signature was running an old input-cleaning step (a magic-quotes-era stripslashes) over the data before signing it. The effect was that two genuinely different field values could be reduced to the same string and therefore produce the same signature, while the replay protection keyed on the raw, un-reduced value. That mismatch is exactly the kind of seam an attacker looks for. Cybersource signs the literal bytes it is sent, so now the module does too: no cleaning step, sign what is actually transmitted, and the signature and the replay guard agree on what they are protecting.

2. Trusting the verdict, not just the signature

A signed reply from Cybersource is authentic, but authentic is not the same as “place the order.” Cybersource’s Decision Manager can return a result that is correctly signed yet carries a review or reject code, meaning the transaction should be held, not fulfilled. The original flow branched on the raw decision string; it now interprets the reply into an explicit outcome (accept, or hold for review) and the controller acts on that interpreted outcome. A signed acceptance that actually carries a review code is now held for a human rather than quietly placed.

3. Closing a replay window under load

The check that stops the same reply being processed twice was a read-then-write, and Commerce’s remote-transaction column is not uniquely constrained, so two near-simultaneous callbacks could in principle both pass the check before either had recorded anything. The check and the record are now wrapped in a Drupal lock keyed to the order and transaction, and it fails closed: if the lock cannot be taken, the request is refused rather than risked.

None of these were dramatic, and that is rather the point. A payment gateway earns trust by being boring in exactly the places that are easy to be subtly wrong: signing, verifying, and not doing the same thing twice. Finding and fixing this class of issue before a release, with a regression test added for each, is what the pre-publication review is for.

Why I built it

The honest answer is that I needed a Cybersource gateway I would be comfortable putting in front of real money, and the existing contributed option did not clear that bar for the kind of work I do. Rather than carry a private patch pile, I wrote a clean implementation from scratch, held it to a stricter standard than contrib usually demands, had it reviewed, and then put it where anyone else with the same problem can use it. Publishing it is partly that: contributing back something I needed anyway. It is also a statement of capability. If you are choosing who should build or look after a Drupal Commerce site that takes card payments, “here is the payment module I wrote, reviewed, and published, go and read it” is a more useful answer than a paragraph of claims.

The project is live on drupal.org now and installable with composer require drupal/cybersource_sop. Formal security-advisory coverage is a separate drupal.org process with its own waiting period, which is under way; until that completes the project carries the standard “not covered by the security advisory policy” note that every new project starts with, and I will update the project page when it changes.

If you run a Drupal shop on Cybersource

If you are on Drupal Commerce 3 and taking, or planning to take, card payments through Cybersource, the module is there to be used, and I would genuinely welcome issues and merge requests on the project page. If you would rather have someone implement it for you, get the Secure Acceptance profile configured correctly (a few of those settings are easy to get subtly wrong), and stand behind the result on a site that handles real payments, that is the work I do. The contact details are below.

© 2026 Graith Internet.

Graith Internet is a UK web development company specialising in PHP, Drupal, and AI-assisted development.