When a card payment gateway ships its libraries only for ASP, PHP and Java, integrating it from Python or Zope is not a matter of bindings: you rewrite the entire handshake with the bank’s server. The protocol, though, does not change with the language. It is an encrypted round-trip between shop and gateway protected by a shared secret, and that is the part to understand before writing a line of code.

The problem

In 2003 anyone selling online in Italy hands the collection to a bank gateway: the shop never sees the card number, which is typed into a page hosted by the bank. Banca Sella’s GestPay system is one of these. The merchant prepares the transaction, the bank gathers the card data on its own domain, authorises with the circuit and returns the outcome to the shop.

The weak point is not the cryptography itself — underneath there is TLS 1.0 (RFC 2246) and the three-domain model of Verified by Visa, live since 2001, which moves cardholder authentication onto the issuer’s domain. The weak point is the boundary between the merchant’s application and the gateway: how the shop declares which amount to collect and which transaction it is authorising, so that the bank does not accept parameters tampered with in the user’s browser.

GestPay resolves this boundary with a shared secret and a double encryption. And the official libraries to do so, at the time, exist for the mainstream languages of corporate web: ASP, PHP, Java. For those building on Python and Zope — the application server from which Plone 1.0 emerges in 2003, sitting on CMF 1.3 — there is nothing. The code has to be written.

Architecture of the round-trip

The flow has three moments, and none of the three passes the card number through the shop.

  1. Encryption. The merchant assembles the transaction parameters — shop code, amount, currency, transaction identifier, buyer data — and sends them to the GestPay server with a crypt call. The gateway replies with an encrypted string: in the proprietary format it is wrapped between #cryptstring# tags. This string, not the cleartext parameters, is what the user’s browser carries to the bank’s domain.
  2. Payment. On the gateway’s domain the user enters the card data. Cardholder authentication, authorisation on the circuit and collection of the PAN all happen outside the shop.
  3. Decryption. Once an outcome is reached the gateway sends the merchant back an encrypted string, wrapped between #decryptstring# tags. The shop passes it to a decrypt call to the GestPay server and extracts the outcome, the authorisation code, any errors and alerts.

The shop neither encrypts nor decrypts on its own: it delegates both operations to the bank’s server, the only party holding the key tied to the shop code. The shared secret is not a key circulating in the merchant’s code; it is the relationship between shop code and gateway. The transport of both calls is HTTP GET to the ecomm.sella.it servers.

A Python library that wraps all of this — the class that calls crypt, the one that calls decrypt, the parsing of the response into object attributes (amount, status, error code, masked card data) — is exactly what noze released in 2003 as open source with GestPayCrypt and the GestPayCryptHS variant, down to the Zope/CMF package to graft the payment into Plone portals: https://www.noze.it/en/open-source/pygestpaycrypt/.

The critical point: the GET

Transporting over HTTP GET is not a neutral choice. A query-string parameter ends up in the web server’s logs, in intermediate proxy logs, in the browser’s history and, if the page carries external links, in the Referer header sent to third parties. Section 15 of RFC 2616 flags precisely these channels: a URL is not a place to put sensitive data.

In the GestPay model this risk is contained by how the flow is designed, not by the transport. What travels on the browser’s query string is the string already encrypted by the gateway, not the cleartext parameters: whoever reads the logs sees #cryptstring#…, not the amount or the buyer data. The crypt and decrypt calls carrying cleartext parameters happen server-to-server, from merchant to gateway, over TLS — not from the browser. And the datum the regulation truly protects, the PAN, never transits the shop: the bank’s domain collects it. The separation of domains keeps the merchant from ever holding the card number — the condition that in 2001 Visa’s CISP programme, later folded into the PCI Data Security Standard 1.0 of December 2001, places at the centre of the seller’s obligations.

It remains that GET is a choice to handle with care: any extra parameter an integration might place in the query string beyond the encrypted string — a session identifier, a cleartext order reference — would fall back into the channels of RFC 2616. The library must push everything sensitive through the encryption, and leave in the clear only what is already public.

Implications

The first implication is that the SDK’s language does not describe the security model. Rewriting the client in Python weakens nothing as long as three invariants hold: encryption on the gateway side, PAN outside the shop, sensitive parameters never in cleartext on the query string. A third-party library that keeps them is, on the security plane, equivalent to the official SDK; one that broke them — for example by encrypting client-side with a hardcoded key — would be dangerous even if written in Java.

The second is that open source here has operational value, not ideological. An inspectable payment library lets you verify that the key is not in the code, that sensitive parameters pass through the encryption, that the response is validated before an order is marked as paid. On a component that decides whether to collect money, being able to read the code is an audit guarantee. The library was distributed under the LGPL — in the form of the GNU Lesser General Public License 2.1 of February 1999, which permits its use in proprietary applications provided modifications to the library remain open.

Limits

The model describes the boundary between shop and gateway, not the entire attack surface of an e-commerce. Left out are the merchant-side validation of the outcome — a shop trusting only the return redirect, without re-checking the decrypt string, could be bypassed by manipulating the browser — the handling of duplicate transactions, accounting reconciliation, and the protection of the page that invokes the encryption. And the proprietary #cryptstring# tag format ties the integration to one specific gateway: switching banks means rewriting the client, not reconfiguring it. The library makes GestPay reachable from Python; it does not make online payment a solved problem.


Cover image: PyGestPayCrypt project logo (noze).