Skip to main content

Swish - Python client

Client library to integrate with the swish commerce and payout api.

Supported APIs

Recurring and QR support is experimental in 0.4.0. Contract validation and stabilization are deferred to 0.5.0; these interfaces may change. Commerce and Payout are not experimental.

Installation

pip install getswish

Prerequisites

Swish api integration require communication certificates and keys to integrate with the commerce api and additional signing certificates and keys for signing payout in the payout api. The client setup require that all the certificates are available through file paths which are given on client setup. The communication certificates are create through your bank portal after the appropriate agreements are signed and signing certificates are set up after additional payout agreements are signed, trough https://portal.swish.nu/.

The signing certificate, private key and serial is only required when using the payout API.

Example - Test Client

The example uses the public MSS 3.0 test credentials bundled with the installed library, valid until September 11, 2027. Do not use these credentials in production. Source: the MSS 3.0 bundle from Swish test environments.

import getswish

swish_client = getswish.SwishClient()

callback_url = "https://example.com/callback/"

All requests default to a 10-second connect timeout and a 30-second read timeout. Configure these with getswish.SwishClient(timeout=(5.0, 60.0)), use a single number for both, or set timeout=None to disable timeouts. The read timeout limits waiting between received data, not the total request duration. Timeouts raise Requests exceptions, not SwishError. After a payment or payout timeout, check its status before retrying; the server may have accepted it.

Example - Commerce API

# Perform a payment request - M-commerce.
payment_m = swish_client.create_payment(
    100.00, callback_url, message="Product name."
)

# Perform a payment request - E-commerce.
payment_e = swish_client.create_payment(
    100.00, callback_url, "46701234567", message="Product name."
)

# Retrieve info about the payment
payment_retrieved = swish_client.retrieve_payment(payment_e.id)

# Cancel the payment
payment_cancelled = swish_client.cancel_payment(payment_e.id)

# After the callback confirms payment_m is PAID, retrieve its payment reference and payer.
paid_payment = swish_client.retrieve_payment(payment_m.id)

# Refund the whole amount to the previous payer, who is now the refund payee.
payment_refund = swish_client.create_refund(
    paid_payment.payment_reference, callback_url, paid_payment.payer_alias, paid_payment.amount
)

# Retrieve info about the refund
payment_refund_retrieved = swish_client.retrieve_refund(payment_refund.id)

Example - Recurring Payments API (experimental)

Recurring payments allow a merchant to charge a customer on a consent basis. The customer signs a consent once (via e-commerce or m-commerce flow), and the merchant can then initiate payments against that consent without further customer interaction.

# Check if the payer's bank supports recurring payments for their alias.
availability = swish_client.check_recurring_alias_availability("46701234567")
if not availability.recurring_is_offered:
    raise Exception("Recurring payments not available for this payer.")

# Create a recurring consent - E-commerce flow (payer alias known).
consent = swish_client.create_recurring_ecommerce_consent(
    callback_url, "46701234567", payee_payment_reference="subscription-001"
)

# Create a recurring consent - M-commerce flow (payer alias unknown; token shown to user).
consent_m = swish_client.create_recurring_mcommerce_consent(
    callback_url, payee_payment_reference="subscription-001"
)
# consent_m.payment_request_token -- show this to the payer in the Swish app.

# Retrieve consent info (e.g. after callback confirms ACTIVE status).
consent_active = swish_client.retrieve_recurring_consent(consent.consent_id)

# Charge the customer against the active consent.
payment = swish_client.create_recurring_payment(
    consent.consent_id, 100.00, callback_url, message="Monthly subscription."
)

# Check payment status.
payment_status = swish_client.retrieve_recurring_payment(payment.payment_id)

# Update the callback URL on an existing consent.
swish_client.update_recurring_consent_callback_url(consent.consent_id, "https://example.com/new-callback/")

# Delete (cancel) the consent.
swish_client.delete_recurring_consent(consent.consent_id)

Example - QR Code API (experimental)

# Generate a prefilled QR code (payee + amount pre-set, editable by payer).
qr_png = swish_client.create_prefilled_qr_code(
    "png",
    payee="1234679304",
    amount=100.00,
    amount_editable=True,
    message="Order #1234",
    size=300,
)
# qr_png is bytes -- write to a file or serve as an image response.

# Generate an SVG QR code.
qr_svg = swish_client.create_prefilled_qr_code("svg", payee="1234679304")
# qr_svg is a str containing the SVG markup.

# Create a fresh M-commerce request without a payer alias; use its token while pending.
qr_payment = swish_client.create_payment(100.00, callback_url, message="QR purchase.")

# Generate a QR code from this Commerce payment token, not a recurring consent token.
qr_commerce = swish_client.create_commerce_qr_code(
    qr_payment.payment_request_token, "png", size=300
)

Example - Payout API

# Generate a merchant specific reference.
# This reference could be order id or similar.
# Use generate_transaction_id for convenience.

from getswish.utils import generate_transaction_id

reference_id = generate_transaction_id()

# Perform a payment request
payout = swish_client.create_payout(
    reference_id, "46701234567", "197001019876", 10.00, callback_url, message="Test payout message."
)

# Retrieve info about the payout
payout_retrieved = swish_client.retrieve_payout(payout.payout_instruction_uuid)

Example - Custom Test Client

This example explicitly uses getswish.TestEnvironment and the bundled test certificates. For production, use getswish.ProductionEnvironment, your issued communication certificate and key, the appropriate TLS trust certificate, and your merchant Swish number. Do not use the bundled test credentials in production.

from pathlib import Path
import getswish

cert_base = Path(getswish.__file__).resolve().parent / "certificates" / "mss_test_3.0"

swish_client = getswish.SwishClient(
    environment=getswish.TestEnvironment,
    certificates=getswish.Certificates(
        communication=getswish.Certificate(
            public=f"{cert_base}/Swish_Merchant_TestCertificate_1234679304.pem",
            private_key=f"{cert_base}/Swish_Merchant_TestCertificate_1234679304.key",
        ),
        verify=getswish.Certificate(public=f"{cert_base}/Swish_TLS_RootCA.pem"),
    ),
    merchant_swish_number="1234679304",
)

Example - Custom Test Client with Payout

This is also a test configuration. Payout additionally requires a signing certificate, private key, and serial number. For production, use getswish.ProductionEnvironment and your issued communication and signing credentials instead of these bundled test files.

from pathlib import Path
import getswish

cert_base = Path(getswish.__file__).resolve().parent / "certificates" / "mss_test_3.0"

swish_client = getswish.SwishClient(
    environment=getswish.TestEnvironment,
    certificates=getswish.Certificates(
        communication=getswish.Certificate(
            public=f"{cert_base}/Swish_Merchant_TestCertificate_1234679304.pem",
            private_key=f"{cert_base}/Swish_Merchant_TestCertificate_1234679304.key",
        ),
        verify=getswish.Certificate(public=f"{cert_base}/Swish_TLS_RootCA.pem"),
        signing=getswish.Certificate(
            public=f"{cert_base}/Swish_Merchant_TestSigningCertificate_1234679304.pem",
            private_key=f"{cert_base}/Swish_Merchant_TestSigningCertificate_1234679304.key",
            public_serial="4F24C03A0295A0B53596240EA8C0F430",
        ),
    ),
    merchant_swish_number="1234679304",
)

callback_url = "https://example.com/callback/"

Generating public_serial for signing certificate

The signing certificate public_serial is extracted from the certificate using this command on linux.

openssl x509 -in Swish_Merchant_TestSigningCertificate_1234679304.pem -serial -noout

Error handling

All HTTP 4xx/5xx responses raise getswish.SwishError, including responses with empty bodies. It inherits from requests.HTTPError (and requests.RequestException). Inspect errors for normalized error messages, raw_errors for the unchanged parsed JSON or response text, trace_id when supplied, and response.status_code to distinguish rejections, authentication failures, rate limits, and server errors. Unexpected error fields are converted to strings; empty errors use an HTTP status message without changing raw_errors. Connection failures, timeouts, and TLS errors retain their Requests exception types.

import requests
import getswish

try:
    payment = swish_client.create_payment(100.00, callback_url)
except getswish.SwishError as error:
    print(error.response.status_code, error.errors, error.trace_id)
except requests.RequestException as error:
    print("Request failed:", error)

Development setup

Install uv (CI uses uv 0.13.1), clone the repository, and sync the locked development environment. uv installs the required Python if necessary.

git clone https://github.com/nibon/getswish-python.git && cd getswish-python

uv sync --locked

Testing

Run all tests.

uv run --locked pytest

Run one focused test.

uv run --locked pytest tests/test_client.py::test_requests_create_payment

Run coverage the same way as CI.

uv run --locked pytest --cov=src

Run the source lint check.

uv run --locked ruff check src

Run the source-only type check (Python 3.10 is the minimum supported version).

uv run --locked ty check src

Build and check the package.

uv build
uv run --locked twine check dist/getswish-*.whl dist/getswish-*.tar.gz

To test another interpreter locally, run uv run --locked --python 3.10 pytest (or 3.11-3.15). CI runs the full six-version matrix; uv does not provide a single-command matrix runner.

The lockfile is for development; library consumers use the dependency ranges in pyproject.toml.

Release workflow

Update the changelog, then bump the version with uv version --bump patch (or minor or major). uv normally updates pyproject.toml and uv.lock and syncs the environment; it does not commit or tag. Review the diff and commit the version, lockfile, and changelog with ordinary Git commands. To keep unrelated staged changes out of the release commit, specify paths with git commit --only -- pyproject.toml uv.lock CHANGELOG.md. CI runs the full matrix, coverage, lint, types, build, distribution validation, and installed-resource checks.

uv build
uv run --locked twine check dist/getswish-*.whl dist/getswish-*.tar.gz

Review the changes and wait for CI. uv publish uploads existing artifacts; it does not build them. Select only the artifacts for the current version, not all files under dist/. For example, for version 0.4.0:

uv publish --index testpypi dist/getswish-0.4.0-py3-none-any.whl dist/getswish-0.4.0.tar.gz
uv publish dist/getswish-0.4.0-py3-none-any.whl dist/getswish-0.4.0.tar.gz

Set UV_PUBLISH_TOKEN to the appropriate API token for each destination (TestPyPI and PyPI use different tokens). The testpypi publish index is configured in pyproject.toml. Never reuse a TestPyPI token on PyPI.

Metadata

Release files for getswish 0.4.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for getswish 0.4.1
File Size Uploaded Provenance
getswish-0.4.1.tar.gz 39.1 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for getswish 0.4.1
File Interpreter ABI Platform
getswish-0.4.1-py3-none-any.whl Python 3 none any Details

Total release size: 68.6 kB

Release files / getswish-0.4.1.tar.gz

Download URL getswish-0.4.1.tar.gz
Size 39.1 kB
Tags Source
SHA-256 checksum
How to use checksums
a664d13ca3ad0634e810882a03653abf64bbaf3a73ec71cf4283a598e9ddbf88
BLAKE2b-256 checksum
How to use checksums
4594f6a45499283bf2b3135e9788b3fbb114e1d7143fad201b75b8b0252a1074
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.13.4 {"installer":{"name":"uv","version":"0.13.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Fedora Linux","version":"44","id":"","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / getswish-0.4.1-py3-none-any.whl

Download URL getswish-0.4.1-py3-none-any.whl
Size 29.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
9cafb02955f26437f17b26a7c618304a4693b146fd07a0b3ad245c74bc010c72
BLAKE2b-256 checksum
How to use checksums
0d4cfaf0e1c49d2bec40b9cf7728b66d671e8bc8355431965cca9f2c7b11da88
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.13.4 {"installer":{"name":"uv","version":"0.13.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Fedora Linux","version":"44","id":"","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Security for getswish 0.4.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page