Repository files navigation

MultiSafepay Python SDK

Code QualityCodecovLicenseLatest stable versionPython versions

Easily integrate MultiSafepay's payment solutions into your Python applications with this official API client. This SDK provides convenient access to the MultiSafepay REST API, supports all core payment features, and is designed for seamless integration into any Python-based backend.

About MultiSafepay

MultiSafepay is a Dutch payment services provider, which takes care of contracts, processing transactions, and collecting payment for a range of local and international payment methods. Start selling online today and manage all your transactions in one place!

Installation

If you want to use the built-in default transport, install with the requests extra.

pip install "multisafepay[requests]"

If you want to provide your own transport implementation, install the base package.

pip install multisafepay

HTTP client / transport (optional dependency)

WARNING: This SDK does not have a hard dependency on a specific HTTP client.

The SDK uses a small transport abstraction so you can choose (and swap) the underlying HTTP implementation without affecting the rest of your integration.

How it works

  • The SDK expects an object implementing the HTTPTransport / HTTPResponse protocols defined in src/multisafepay/transport/http_transport.py.
  • Event stream subscriptions additionally require the transport to implement the HTTPStreamingTransport protocol (adds open_stream(...) returning an HTTPStreamResponse with readline(), close(), and raise_for_status()).
  • If you do not provide a transport, the SDK defaults to RequestsTransport.
  • requests is an optional extra:
    • To use the default transport, install multisafepay[requests].
    • To avoid requests, inject your own transport (for example, httpx or urllib3).

The built-in RequestsTransport implements both HTTPTransport and HTTPStreamingTransport, so the same configured requests.Session is reused for regular requests and SSE streams. Custom transports that only implement HTTPTransport (request(...)) can still be used for regular API calls, but SSE subscriptions fail explicitly until they also implement HTTPStreamingTransport. The SDK does not fall back to another HTTP library for event streams.

Custom transport example

pip install multisafepay
frommultisafepayimportSdksdk=Sdk(
api_key="<api_key>",
is_production=False,
transport=my_custom_transport, # must implement HTTPTransport
)

See transport examples in examples/transport/ (httpx_transport.py, urllib3_transport.py, request_transport.py).

Getting started

Initialize the client

frommultisafepayimportSdkmultisafepay_sdk: Sdk=Sdk(api_key='<api_key>', is_production=True)

Initialize with scoped credentials

Use ScopedCredentialResolver when different API keys must be selected per auth scope. When credential_resolver is provided, api_key becomes optional.

frommultisafepayimportSdkfrommultisafepay.clientimportScopedCredentialResolvercredential_resolver=ScopedCredentialResolver(
default_api_key="<default_api_key>",
partner_affiliate_api_key="<partner_api_key>",
terminal_group_api_keys={
"<terminal_group_id>": "<terminal_group_api_key>",
},
)
sdk=Sdk(
is_production=False,
credential_resolver=credential_resolver,
)

Event stream subscriptions

Use EventManager to subscribe to MultiSafepay SSE streams directly, or to subscribe from an order response that already contains event credentials.

frommultisafepayimportSdkfrommultisafepay.clientimportScopedCredentialResolvercredential_resolver=ScopedCredentialResolver(
default_api_key="<default_api_key>",
terminal_group_api_keys={
"<terminal_group_id>": "<terminal_group_api_key>",
},
)
sdk=Sdk(
is_production=False,
credential_resolver=credential_resolver,
)
order_manager=sdk.get_order_manager()
event_manager=sdk.get_event_manager()
# Build your OrderRequest here, for example:# from multisafepay.api.paths.orders.request.order_request import OrderRequest# order_request = OrderRequest(...)create_response=order_manager.create(
request_order=order_request,
terminal_group_id="<terminal_group_id>",
)
order=create_response.get_data()
withevent_manager.subscribe_order_events(order, timeout=45.0) asstream:
foreventinstream:
print(event)

Cloud POS order tips

For POS and Cloud POS integrations, you can send tip information as part of the order creation payload with amount_details. Amount values are expressed in the smallest currency unit, so this example sends a total order amount of EUR 1.20 with EUR 0.20 marked as tip.

frommultisafepay.api.paths.orders.requestimportOrderRequestfrommultisafepay.api.paths.orders.request.componentsimport (
AmountDetails,
Tip,
)
order_request= (
OrderRequest()
.add_type("redirect")
.add_order_id("cloud-pos-order-with-tip")
.add_description("Cloud POS order with tip")
.add_amount(120)
.add_currency("EUR")
.add_gateway_info({"terminal_id": "<terminal_id>"})
.add_amount_details(
AmountDetails().add_tip(Tip().add_amount(20)),
)
)

The amount_details field serializes to:

{
"amount_details": {
"tip": {
"amount": 20
}
}
}

See the full Cloud POS tip example in examples/order_manager/cloud_pos_order_with_tip.py.

Development-only custom base URL override

By default, the SDK only targets:

  • test: https://testapi.multisafepay.com/v1/
  • live: https://api.multisafepay.com/v1/

For local development, a custom base URL can be enabled with strict guardrails:

export MSP_SDK_BUILD_PROFILE=dev
export MSP_SDK_ALLOW_CUSTOM_BASE_URL=1

You can provide the custom base URL either via environment variable or via the SDK argument.

Environment variable option:

export MSP_SDK_CUSTOM_BASE_URL="https://dev-api.example.com/v1"

SDK argument option:

frommultisafepayimportSdksdk=Sdk(
api_key="<api_key>",
is_production=False,
base_url="https://dev-api.example.com/v1",
)

Precedence when both are set:

  • The explicit SDK argument base_url takes priority.
  • If base_url is not passed, MSP_SDK_CUSTOM_BASE_URL is used.

In any non-dev profile (including default release), custom base URLs are blocked and the SDK will only use test/live URLs.

Examples

Go to the folder examples to see how to use the SDK.

The event-stream example in examples/event_manager/subscribe_events.py requires:

export API_KEY="<account_api_key>"export TERMINAL_GROUP_API_KEY_GROUP_DEFAULT="<terminal_group_api_key>"export CLOUD_POS_TERMINAL_GROUP_ID="<terminal_group_id>"export CLOUD_POS_TERMINAL_ID="<terminal_id>"

The SSE E2E test can also run against a dev-backed base URL and optionally resolve the terminal group automatically:

export E2E_NO_SANDBOX_BASE_URL="https://dev-api.example.com/v1/"export MSP_SDK_BUILD_PROFILE=dev
export MSP_SDK_ALLOW_CUSTOM_BASE_URL=1
export MSP_SDK_CUSTOM_BASE_URL="https://dev-api.example.com/v1/"export E2E_API_KEY="<account_api_key>"export E2E_TERMINAL_GROUP_API_KEY_GROUP_DEFAULT="<terminal_group_api_key>"export E2E_CLOUD_POS_TERMINAL_ID="<terminal_id>"# Optional when CLOUD_POS_TERMINAL_GROUP_ID is not setexport E2E_PARTNER_API_KEY="<partner_api_key>"

Code quality checks

Linting

make lint

Testing

make test

E2E target environment

By default, E2E tests target https://testapi.multisafepay.com/v1/.

Use dedicated E2E variables instead of the general SDK variables:

export E2E_API_KEY="<test_api_key>"export E2E_BASE_URL="https://testapi.multisafepay.com/v1/"# optional
make test-e2e

E2E_BASE_URL is optional and can point to any HTTPS base URL used for E2E. When omitted, E2E defaults to testapi.multisafepay.com.

The e2e suite does not use the shared API_KEY variable or the shared MSP_SDK_* custom base URL settings.

Terminal endpoint examples and E2E checks use a dev-backed base URL because those endpoints are not exercised against the default shared E2E target.

export API_KEY="<account_api_key>"export PARTNER_API_KEY="<partner_api_key>"# optionalexport MSP_SDK_BUILD_PROFILE=dev
export MSP_SDK_ALLOW_CUSTOM_BASE_URL=1
export MSP_SDK_CUSTOM_BASE_URL="https://dev-api.example.com/v1/"export E2E_CLOUD_POS_TERMINAL_ID="<terminal_id>"# Optional: set when you want to skip automatic terminal-group lookupexport CLOUD_POS_TERMINAL_GROUP_ID="<terminal_group_id>"
make test-e2e

Support

Create an issue on this repository or email integration@multisafepay.com

Contributors

If you create a pull request to suggest an improvement, we'll send you some MultiSafepay swag as a thank you!

License

Open Software License (OSL 3.0)

Want to be part of the team?

Are you a developer interested in working at MultiSafepay? Check out our job openings and feel free to get in touch!

About

The default Python library for connecting to the MultiSafepay REST API

Resources

Code of conduct

Contributing

Stars

0 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

MultiSafepay Python SDK

Code QualityCodecovLicenseLatest stable versionPython versions

Easily integrate MultiSafepay's payment solutions into your Python applications with this official API client. This SDK provides convenient access to the MultiSafepay REST API, supports all core payment features, and is designed for seamless integration into any Python-based backend.

About MultiSafepay

MultiSafepay is a Dutch payment services provider, which takes care of contracts, processing transactions, and collecting payment for a range of local and international payment methods. Start selling online today and manage all your transactions in one place!

Installation

If you want to use the built-in default transport, install with the requests extra.

pip install "multisafepay[requests]"

If you want to provide your own transport implementation, install the base package.

pip install multisafepay

HTTP client / transport (optional dependency)

WARNING: This SDK does not have a hard dependency on a specific HTTP client.

The SDK uses a small transport abstraction so you can choose (and swap) the underlying HTTP implementation without affecting the rest of your integration.

How it works

  • The SDK expects an object implementing the HTTPTransport / HTTPResponse protocols defined in src/multisafepay/transport/http_transport.py.
  • Event stream subscriptions additionally require the transport to implement the HTTPStreamingTransport protocol (adds open_stream(...) returning an HTTPStreamResponse with readline(), close(), and raise_for_status()).
  • If you do not provide a transport, the SDK defaults to RequestsTransport.
  • requests is an optional extra:
    • To use the default transport, install multisafepay[requests].
    • To avoid requests, inject your own transport (for example, httpx or urllib3).

The built-in RequestsTransport implements both HTTPTransport and HTTPStreamingTransport, so the same configured requests.Session is reused for regular requests and SSE streams. Custom transports that only implement HTTPTransport (request(...)) can still be used for regular API calls, but SSE subscriptions fail explicitly until they also implement HTTPStreamingTransport. The SDK does not fall back to another HTTP library for event streams.

Custom transport example

pip install multisafepay
frommultisafepayimportSdksdk=Sdk(
api_key="<api_key>",
is_production=False,
transport=my_custom_transport, # must implement HTTPTransport
)

See transport examples in examples/transport/ (httpx_transport.py, urllib3_transport.py, request_transport.py).

Getting started

Initialize the client

frommultisafepayimportSdkmultisafepay_sdk: Sdk=Sdk(api_key='<api_key>', is_production=True)

Initialize with scoped credentials

Use ScopedCredentialResolver when different API keys must be selected per auth scope. When credential_resolver is provided, api_key becomes optional.

frommultisafepayimportSdkfrommultisafepay.clientimportScopedCredentialResolvercredential_resolver=ScopedCredentialResolver(
default_api_key="<default_api_key>",
partner_affiliate_api_key="<partner_api_key>",
terminal_group_api_keys={
"<terminal_group_id>": "<terminal_group_api_key>",
},
)
sdk=Sdk(
is_production=False,
credential_resolver=credential_resolver,
)

Event stream subscriptions

Use EventManager to subscribe to MultiSafepay SSE streams directly, or to subscribe from an order response that already contains event credentials.

frommultisafepayimportSdkfrommultisafepay.clientimportScopedCredentialResolvercredential_resolver=ScopedCredentialResolver(
default_api_key="<default_api_key>",
terminal_group_api_keys={
"<terminal_group_id>": "<terminal_group_api_key>",
},
)
sdk=Sdk(
is_production=False,
credential_resolver=credential_resolver,
)
order_manager=sdk.get_order_manager()
event_manager=sdk.get_event_manager()
# Build your OrderRequest here, for example:# from multisafepay.api.paths.orders.request.order_request import OrderRequest# order_request = OrderRequest(...)create_response=order_manager.create(
request_order=order_request,
terminal_group_id="<terminal_group_id>",
)
order=create_response.get_data()
withevent_manager.subscribe_order_events(order, timeout=45.0) asstream:
foreventinstream:
print(event)

Cloud POS order tips

For POS and Cloud POS integrations, you can send tip information as part of the order creation payload with amount_details. Amount values are expressed in the smallest currency unit, so this example sends a total order amount of EUR 1.20 with EUR 0.20 marked as tip.

frommultisafepay.api.paths.orders.requestimportOrderRequestfrommultisafepay.api.paths.orders.request.componentsimport (
AmountDetails,
Tip,
)
order_request= (
OrderRequest()
.add_type("redirect")
.add_order_id("cloud-pos-order-with-tip")
.add_description("Cloud POS order with tip")
.add_amount(120)
.add_currency("EUR")
.add_gateway_info({"terminal_id": "<terminal_id>"})
.add_amount_details(
AmountDetails().add_tip(Tip().add_amount(20)),
)
)

The amount_details field serializes to:

{
"amount_details": {
"tip": {
"amount": 20
}
}
}

See the full Cloud POS tip example in examples/order_manager/cloud_pos_order_with_tip.py.

Development-only custom base URL override

By default, the SDK only targets:

  • test: https://testapi.multisafepay.com/v1/
  • live: https://api.multisafepay.com/v1/

For local development, a custom base URL can be enabled with strict guardrails:

export MSP_SDK_BUILD_PROFILE=dev
export MSP_SDK_ALLOW_CUSTOM_BASE_URL=1

You can provide the custom base URL either via environment variable or via the SDK argument.

Environment variable option:

export MSP_SDK_CUSTOM_BASE_URL="https://dev-api.example.com/v1"

SDK argument option:

frommultisafepayimportSdksdk=Sdk(
api_key="<api_key>",
is_production=False,
base_url="https://dev-api.example.com/v1",
)

Precedence when both are set:

  • The explicit SDK argument base_url takes priority.
  • If base_url is not passed, MSP_SDK_CUSTOM_BASE_URL is used.

In any non-dev profile (including default release), custom base URLs are blocked and the SDK will only use test/live URLs.

Examples

Go to the folder examples to see how to use the SDK.

The event-stream example in examples/event_manager/subscribe_events.py requires:

export API_KEY="<account_api_key>"export TERMINAL_GROUP_API_KEY_GROUP_DEFAULT="<terminal_group_api_key>"export CLOUD_POS_TERMINAL_GROUP_ID="<terminal_group_id>"export CLOUD_POS_TERMINAL_ID="<terminal_id>"

The SSE E2E test can also run against a dev-backed base URL and optionally resolve the terminal group automatically:

export E2E_NO_SANDBOX_BASE_URL="https://dev-api.example.com/v1/"export MSP_SDK_BUILD_PROFILE=dev
export MSP_SDK_ALLOW_CUSTOM_BASE_URL=1
export MSP_SDK_CUSTOM_BASE_URL="https://dev-api.example.com/v1/"export E2E_API_KEY="<account_api_key>"export E2E_TERMINAL_GROUP_API_KEY_GROUP_DEFAULT="<terminal_group_api_key>"export E2E_CLOUD_POS_TERMINAL_ID="<terminal_id>"# Optional when CLOUD_POS_TERMINAL_GROUP_ID is not setexport E2E_PARTNER_API_KEY="<partner_api_key>"

Code quality checks

Linting

make lint

Testing

make test

E2E target environment

By default, E2E tests target https://testapi.multisafepay.com/v1/.

Use dedicated E2E variables instead of the general SDK variables:

export E2E_API_KEY="<test_api_key>"export E2E_BASE_URL="https://testapi.multisafepay.com/v1/"# optional
make test-e2e

E2E_BASE_URL is optional and can point to any HTTPS base URL used for E2E. When omitted, E2E defaults to testapi.multisafepay.com.

The e2e suite does not use the shared API_KEY variable or the shared MSP_SDK_* custom base URL settings.

Terminal endpoint examples and E2E checks use a dev-backed base URL because those endpoints are not exercised against the default shared E2E target.

export API_KEY="<account_api_key>"export PARTNER_API_KEY="<partner_api_key>"# optionalexport MSP_SDK_BUILD_PROFILE=dev
export MSP_SDK_ALLOW_CUSTOM_BASE_URL=1
export MSP_SDK_CUSTOM_BASE_URL="https://dev-api.example.com/v1/"export E2E_CLOUD_POS_TERMINAL_ID="<terminal_id>"# Optional: set when you want to skip automatic terminal-group lookupexport CLOUD_POS_TERMINAL_GROUP_ID="<terminal_group_id>"
make test-e2e

Support

Create an issue on this repository or email integration@multisafepay.com

Contributors

If you create a pull request to suggest an improvement, we'll send you some MultiSafepay swag as a thank you!

License

Open Software License (OSL 3.0)

Want to be part of the team?

Are you a developer interested in working at MultiSafepay? Check out our job openings and feel free to get in touch!

About

The default Python library for connecting to the MultiSafepay REST API

Resources

Code of conduct

Contributing

Stars

0 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

MultiSafepay Python SDK

Code QualityCodecovLicenseLatest stable versionPython versions

Easily integrate MultiSafepay's payment solutions into your Python applications with this official API client. This SDK provides convenient access to the MultiSafepay REST API, supports all core payment features, and is designed for seamless integration into any Python-based backend.

About MultiSafepay

MultiSafepay is a Dutch payment services provider, which takes care of contracts, processing transactions, and collecting payment for a range of local and international payment methods. Start selling online today and manage all your transactions in one place!

Installation

If you want to use the built-in default transport, install with the requests extra.

pip install "multisafepay[requests]"

If you want to provide your own transport implementation, install the base package.

pip install multisafepay

HTTP client / transport (optional dependency)

WARNING: This SDK does not have a hard dependency on a specific HTTP client.

The SDK uses a small transport abstraction so you can choose (and swap) the underlying HTTP implementation without affecting the rest of your integration.

How it works

  • The SDK expects an object implementing the HTTPTransport / HTTPResponse protocols defined in src/multisafepay/transport/http_transport.py.
  • Event stream subscriptions additionally require the transport to implement the HTTPStreamingTransport protocol (adds open_stream(...) returning an HTTPStreamResponse with readline(), close(), and raise_for_status()).
  • If you do not provide a transport, the SDK defaults to RequestsTransport.
  • requests is an optional extra:
    • To use the default transport, install multisafepay[requests].
    • To avoid requests, inject your own transport (for example, httpx or urllib3).

The built-in RequestsTransport implements both HTTPTransport and HTTPStreamingTransport, so the same configured requests.Session is reused for regular requests and SSE streams. Custom transports that only implement HTTPTransport (request(...)) can still be used for regular API calls, but SSE subscriptions fail explicitly until they also implement HTTPStreamingTransport. The SDK does not fall back to another HTTP library for event streams.

Custom transport example

pip install multisafepay
frommultisafepayimportSdksdk=Sdk(
api_key="<api_key>",
is_production=False,
transport=my_custom_transport, # must implement HTTPTransport
)

See transport examples in examples/transport/ (httpx_transport.py, urllib3_transport.py, request_transport.py).

Getting started

Initialize the client

frommultisafepayimportSdkmultisafepay_sdk: Sdk=Sdk(api_key='<api_key>', is_production=True)

Initialize with scoped credentials

Use ScopedCredentialResolver when different API keys must be selected per auth scope. When credential_resolver is provided, api_key becomes optional.

frommultisafepayimportSdkfrommultisafepay.clientimportScopedCredentialResolvercredential_resolver=ScopedCredentialResolver(
default_api_key="<default_api_key>",
partner_affiliate_api_key="<partner_api_key>",
terminal_group_api_keys={
"<terminal_group_id>": "<terminal_group_api_key>",
},
)
sdk=Sdk(
is_production=False,
credential_resolver=credential_resolver,
)

Event stream subscriptions

Use EventManager to subscribe to MultiSafepay SSE streams directly, or to subscribe from an order response that already contains event credentials.

frommultisafepayimportSdkfrommultisafepay.clientimportScopedCredentialResolvercredential_resolver=ScopedCredentialResolver(
default_api_key="<default_api_key>",
terminal_group_api_keys={
"<terminal_group_id>": "<terminal_group_api_key>",
},
)
sdk=Sdk(
is_production=False,
credential_resolver=credential_resolver,
)
order_manager=sdk.get_order_manager()
event_manager=sdk.get_event_manager()
# Build your OrderRequest here, for example:# from multisafepay.api.paths.orders.request.order_request import OrderRequest# order_request = OrderRequest(...)create_response=order_manager.create(
request_order=order_request,
terminal_group_id="<terminal_group_id>",
)
order=create_response.get_data()
withevent_manager.subscribe_order_events(order, timeout=45.0) asstream:
foreventinstream:
print(event)

Cloud POS order tips

For POS and Cloud POS integrations, you can send tip information as part of the order creation payload with amount_details. Amount values are expressed in the smallest currency unit, so this example sends a total order amount of EUR 1.20 with EUR 0.20 marked as tip.

frommultisafepay.api.paths.orders.requestimportOrderRequestfrommultisafepay.api.paths.orders.request.componentsimport (
AmountDetails,
Tip,
)
order_request= (
OrderRequest()
.add_type("redirect")
.add_order_id("cloud-pos-order-with-tip")
.add_description("Cloud POS order with tip")
.add_amount(120)
.add_currency("EUR")
.add_gateway_info({"terminal_id": "<terminal_id>"})
.add_amount_details(
AmountDetails().add_tip(Tip().add_amount(20)),
)
)

The amount_details field serializes to:

{
"amount_details": {
"tip": {
"amount": 20
}
}
}

See the full Cloud POS tip example in examples/order_manager/cloud_pos_order_with_tip.py.

Development-only custom base URL override

By default, the SDK only targets:

  • test: https://testapi.multisafepay.com/v1/
  • live: https://api.multisafepay.com/v1/

For local development, a custom base URL can be enabled with strict guardrails:

export MSP_SDK_BUILD_PROFILE=dev
export MSP_SDK_ALLOW_CUSTOM_BASE_URL=1

You can provide the custom base URL either via environment variable or via the SDK argument.

Environment variable option:

export MSP_SDK_CUSTOM_BASE_URL="https://dev-api.example.com/v1"

SDK argument option:

frommultisafepayimportSdksdk=Sdk(
api_key="<api_key>",
is_production=False,
base_url="https://dev-api.example.com/v1",
)

Precedence when both are set:

  • The explicit SDK argument base_url takes priority.
  • If base_url is not passed, MSP_SDK_CUSTOM_BASE_URL is used.

In any non-dev profile (including default release), custom base URLs are blocked and the SDK will only use test/live URLs.

Examples

Go to the folder examples to see how to use the SDK.

The event-stream example in examples/event_manager/subscribe_events.py requires:

export API_KEY="<account_api_key>"export TERMINAL_GROUP_API_KEY_GROUP_DEFAULT="<terminal_group_api_key>"export CLOUD_POS_TERMINAL_GROUP_ID="<terminal_group_id>"export CLOUD_POS_TERMINAL_ID="<terminal_id>"

The SSE E2E test can also run against a dev-backed base URL and optionally resolve the terminal group automatically:

export E2E_NO_SANDBOX_BASE_URL="https://dev-api.example.com/v1/"export MSP_SDK_BUILD_PROFILE=dev
export MSP_SDK_ALLOW_CUSTOM_BASE_URL=1
export MSP_SDK_CUSTOM_BASE_URL="https://dev-api.example.com/v1/"export E2E_API_KEY="<account_api_key>"export E2E_TERMINAL_GROUP_API_KEY_GROUP_DEFAULT="<terminal_group_api_key>"export E2E_CLOUD_POS_TERMINAL_ID="<terminal_id>"# Optional when CLOUD_POS_TERMINAL_GROUP_ID is not setexport E2E_PARTNER_API_KEY="<partner_api_key>"

Code quality checks

Linting

make lint

Testing

make test

E2E target environment

By default, E2E tests target https://testapi.multisafepay.com/v1/.

Use dedicated E2E variables instead of the general SDK variables:

export E2E_API_KEY="<test_api_key>"export E2E_BASE_URL="https://testapi.multisafepay.com/v1/"# optional
make test-e2e

E2E_BASE_URL is optional and can point to any HTTPS base URL used for E2E. When omitted, E2E defaults to testapi.multisafepay.com.

The e2e suite does not use the shared API_KEY variable or the shared MSP_SDK_* custom base URL settings.

Terminal endpoint examples and E2E checks use a dev-backed base URL because those endpoints are not exercised against the default shared E2E target.

export API_KEY="<account_api_key>"export PARTNER_API_KEY="<partner_api_key>"# optionalexport MSP_SDK_BUILD_PROFILE=dev
export MSP_SDK_ALLOW_CUSTOM_BASE_URL=1
export MSP_SDK_CUSTOM_BASE_URL="https://dev-api.example.com/v1/"export E2E_CLOUD_POS_TERMINAL_ID="<terminal_id>"# Optional: set when you want to skip automatic terminal-group lookupexport CLOUD_POS_TERMINAL_GROUP_ID="<terminal_group_id>"
make test-e2e

Support

Create an issue on this repository or email integration@multisafepay.com

Contributors

If you create a pull request to suggest an improvement, we'll send you some MultiSafepay swag as a thank you!

License

Open Software License (OSL 3.0)

Want to be part of the team?

Are you a developer interested in working at MultiSafepay? Check out our job openings and feel free to get in touch!

About

The default Python library for connecting to the MultiSafepay REST API

Resources

Code of conduct

Contributing

Stars

0 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

MultiSafepay Python SDK

Code QualityCodecovLicenseLatest stable versionPython versions

Easily integrate MultiSafepay's payment solutions into your Python applications with this official API client. This SDK provides convenient access to the MultiSafepay REST API, supports all core payment features, and is designed for seamless integration into any Python-based backend.

About MultiSafepay

MultiSafepay is a Dutch payment services provider, which takes care of contracts, processing transactions, and collecting payment for a range of local and international payment methods. Start selling online today and manage all your transactions in one place!

Installation

If you want to use the built-in default transport, install with the requests extra.

pip install "multisafepay[requests]"

If you want to provide your own transport implementation, install the base package.

pip install multisafepay

HTTP client / transport (optional dependency)

WARNING: This SDK does not have a hard dependency on a specific HTTP client.

The SDK uses a small transport abstraction so you can choose (and swap) the underlying HTTP implementation without affecting the rest of your integration.

How it works

  • The SDK expects an object implementing the HTTPTransport / HTTPResponse protocols defined in src/multisafepay/transport/http_transport.py.
  • Event stream subscriptions additionally require the transport to implement the HTTPStreamingTransport protocol (adds open_stream(...) returning an HTTPStreamResponse with readline(), close(), and raise_for_status()).
  • If you do not provide a transport, the SDK defaults to RequestsTransport.
  • requests is an optional extra:
    • To use the default transport, install multisafepay[requests].
    • To avoid requests, inject your own transport (for example, httpx or urllib3).

The built-in RequestsTransport implements both HTTPTransport and HTTPStreamingTransport, so the same configured requests.Session is reused for regular requests and SSE streams. Custom transports that only implement HTTPTransport (request(...)) can still be used for regular API calls, but SSE subscriptions fail explicitly until they also implement HTTPStreamingTransport. The SDK does not fall back to another HTTP library for event streams.

Custom transport example

pip install multisafepay
frommultisafepayimportSdksdk=Sdk(
api_key="<api_key>",
is_production=False,
transport=my_custom_transport, # must implement HTTPTransport
)

See transport examples in examples/transport/ (httpx_transport.py, urllib3_transport.py, request_transport.py).

Getting started

Initialize the client

frommultisafepayimportSdkmultisafepay_sdk: Sdk=Sdk(api_key='<api_key>', is_production=True)

Initialize with scoped credentials

Use ScopedCredentialResolver when different API keys must be selected per auth scope. When credential_resolver is provided, api_key becomes optional.

frommultisafepayimportSdkfrommultisafepay.clientimportScopedCredentialResolvercredential_resolver=ScopedCredentialResolver(
default_api_key="<default_api_key>",
partner_affiliate_api_key="<partner_api_key>",
terminal_group_api_keys={
"<terminal_group_id>": "<terminal_group_api_key>",
},
)
sdk=Sdk(
is_production=False,
credential_resolver=credential_resolver,
)

Event stream subscriptions

Use EventManager to subscribe to MultiSafepay SSE streams directly, or to subscribe from an order response that already contains event credentials.

frommultisafepayimportSdkfrommultisafepay.clientimportScopedCredentialResolvercredential_resolver=ScopedCredentialResolver(
default_api_key="<default_api_key>",
terminal_group_api_keys={
"<terminal_group_id>": "<terminal_group_api_key>",
},
)
sdk=Sdk(
is_production=False,
credential_resolver=credential_resolver,
)
order_manager=sdk.get_order_manager()
event_manager=sdk.get_event_manager()
# Build your OrderRequest here, for example:# from multisafepay.api.paths.orders.request.order_request import OrderRequest# order_request = OrderRequest(...)create_response=order_manager.create(
request_order=order_request,
terminal_group_id="<terminal_group_id>",
)
order=create_response.get_data()
withevent_manager.subscribe_order_events(order, timeout=45.0) asstream:
foreventinstream:
print(event)

Cloud POS order tips

For POS and Cloud POS integrations, you can send tip information as part of the order creation payload with amount_details. Amount values are expressed in the smallest currency unit, so this example sends a total order amount of EUR 1.20 with EUR 0.20 marked as tip.

frommultisafepay.api.paths.orders.requestimportOrderRequestfrommultisafepay.api.paths.orders.request.componentsimport (
AmountDetails,
Tip,
)
order_request= (
OrderRequest()
.add_type("redirect")
.add_order_id("cloud-pos-order-with-tip")
.add_description("Cloud POS order with tip")
.add_amount(120)
.add_currency("EUR")
.add_gateway_info({"terminal_id": "<terminal_id>"})
.add_amount_details(
AmountDetails().add_tip(Tip().add_amount(20)),
)
)

The amount_details field serializes to:

{
"amount_details": {
"tip": {
"amount": 20
}
}
}

See the full Cloud POS tip example in examples/order_manager/cloud_pos_order_with_tip.py.

Development-only custom base URL override

By default, the SDK only targets:

  • test: https://testapi.multisafepay.com/v1/
  • live: https://api.multisafepay.com/v1/

For local development, a custom base URL can be enabled with strict guardrails:

export MSP_SDK_BUILD_PROFILE=dev
export MSP_SDK_ALLOW_CUSTOM_BASE_URL=1

You can provide the custom base URL either via environment variable or via the SDK argument.

Environment variable option:

export MSP_SDK_CUSTOM_BASE_URL="https://dev-api.example.com/v1"

SDK argument option:

frommultisafepayimportSdksdk=Sdk(
api_key="<api_key>",
is_production=False,
base_url="https://dev-api.example.com/v1",
)

Precedence when both are set:

  • The explicit SDK argument base_url takes priority.
  • If base_url is not passed, MSP_SDK_CUSTOM_BASE_URL is used.

In any non-dev profile (including default release), custom base URLs are blocked and the SDK will only use test/live URLs.

Examples

Go to the folder examples to see how to use the SDK.

The event-stream example in examples/event_manager/subscribe_events.py requires:

export API_KEY="<account_api_key>"export TERMINAL_GROUP_API_KEY_GROUP_DEFAULT="<terminal_group_api_key>"export CLOUD_POS_TERMINAL_GROUP_ID="<terminal_group_id>"export CLOUD_POS_TERMINAL_ID="<terminal_id>"

The SSE E2E test can also run against a dev-backed base URL and optionally resolve the terminal group automatically:

export E2E_NO_SANDBOX_BASE_URL="https://dev-api.example.com/v1/"export MSP_SDK_BUILD_PROFILE=dev
export MSP_SDK_ALLOW_CUSTOM_BASE_URL=1
export MSP_SDK_CUSTOM_BASE_URL="https://dev-api.example.com/v1/"export E2E_API_KEY="<account_api_key>"export E2E_TERMINAL_GROUP_API_KEY_GROUP_DEFAULT="<terminal_group_api_key>"export E2E_CLOUD_POS_TERMINAL_ID="<terminal_id>"# Optional when CLOUD_POS_TERMINAL_GROUP_ID is not setexport E2E_PARTNER_API_KEY="<partner_api_key>"

Code quality checks

Linting

make lint

Testing

make test

E2E target environment

By default, E2E tests target https://testapi.multisafepay.com/v1/.

Use dedicated E2E variables instead of the general SDK variables:

export E2E_API_KEY="<test_api_key>"export E2E_BASE_URL="https://testapi.multisafepay.com/v1/"# optional
make test-e2e

E2E_BASE_URL is optional and can point to any HTTPS base URL used for E2E. When omitted, E2E defaults to testapi.multisafepay.com.

The e2e suite does not use the shared API_KEY variable or the shared MSP_SDK_* custom base URL settings.

Terminal endpoint examples and E2E checks use a dev-backed base URL because those endpoints are not exercised against the default shared E2E target.

export API_KEY="<account_api_key>"export PARTNER_API_KEY="<partner_api_key>"# optionalexport MSP_SDK_BUILD_PROFILE=dev
export MSP_SDK_ALLOW_CUSTOM_BASE_URL=1
export MSP_SDK_CUSTOM_BASE_URL="https://dev-api.example.com/v1/"export E2E_CLOUD_POS_TERMINAL_ID="<terminal_id>"# Optional: set when you want to skip automatic terminal-group lookupexport CLOUD_POS_TERMINAL_GROUP_ID="<terminal_group_id>"
make test-e2e

Support

Create an issue on this repository or email integration@multisafepay.com

Contributors

If you create a pull request to suggest an improvement, we'll send you some MultiSafepay swag as a thank you!

License

Open Software License (OSL 3.0)

Want to be part of the team?

Are you a developer interested in working at MultiSafepay? Check out our job openings and feel free to get in touch!

About

The default Python library for connecting to the MultiSafepay REST API

Resources

Code of conduct

Contributing

Stars

0 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

MultiSafepay Python SDK

Code QualityCodecovLicenseLatest stable versionPython versions

Easily integrate MultiSafepay's payment solutions into your Python applications with this official API client. This SDK provides convenient access to the MultiSafepay REST API, supports all core payment features, and is designed for seamless integration into any Python-based backend.

About MultiSafepay

MultiSafepay is a Dutch payment services provider, which takes care of contracts, processing transactions, and collecting payment for a range of local and international payment methods. Start selling online today and manage all your transactions in one place!

Installation

If you want to use the built-in default transport, install with the requests extra.

pip install "multisafepay[requests]"

If you want to provide your own transport implementation, install the base package.

pip install multisafepay

HTTP client / transport (optional dependency)

WARNING: This SDK does not have a hard dependency on a specific HTTP client.

The SDK uses a small transport abstraction so you can choose (and swap) the underlying HTTP implementation without affecting the rest of your integration.

How it works

  • The SDK expects an object implementing the HTTPTransport / HTTPResponse protocols defined in src/multisafepay/transport/http_transport.py.
  • Event stream subscriptions additionally require the transport to implement the HTTPStreamingTransport protocol (adds open_stream(...) returning an HTTPStreamResponse with readline(), close(), and raise_for_status()).
  • If you do not provide a transport, the SDK defaults to RequestsTransport.
  • requests is an optional extra:
    • To use the default transport, install multisafepay[requests].
    • To avoid requests, inject your own transport (for example, httpx or urllib3).

The built-in RequestsTransport implements both HTTPTransport and HTTPStreamingTransport, so the same configured requests.Session is reused for regular requests and SSE streams. Custom transports that only implement HTTPTransport (request(...)) can still be used for regular API calls, but SSE subscriptions fail explicitly until they also implement HTTPStreamingTransport. The SDK does not fall back to another HTTP library for event streams.

Custom transport example

pip install multisafepay
frommultisafepayimportSdksdk=Sdk(
api_key="<api_key>",
is_production=False,
transport=my_custom_transport, # must implement HTTPTransport
)

See transport examples in examples/transport/ (httpx_transport.py, urllib3_transport.py, request_transport.py).

Getting started

Initialize the client

frommultisafepayimportSdkmultisafepay_sdk: Sdk=Sdk(api_key='<api_key>', is_production=True)

Initialize with scoped credentials

Use ScopedCredentialResolver when different API keys must be selected per auth scope. When credential_resolver is provided, api_key becomes optional.

frommultisafepayimportSdkfrommultisafepay.clientimportScopedCredentialResolvercredential_resolver=ScopedCredentialResolver(
default_api_key="<default_api_key>",
partner_affiliate_api_key="<partner_api_key>",
terminal_group_api_keys={
"<terminal_group_id>": "<terminal_group_api_key>",
},
)
sdk=Sdk(
is_production=False,
credential_resolver=credential_resolver,
)

Event stream subscriptions

Use EventManager to subscribe to MultiSafepay SSE streams directly, or to subscribe from an order response that already contains event credentials.

frommultisafepayimportSdkfrommultisafepay.clientimportScopedCredentialResolvercredential_resolver=ScopedCredentialResolver(
default_api_key="<default_api_key>",
terminal_group_api_keys={
"<terminal_group_id>": "<terminal_group_api_key>",
},
)
sdk=Sdk(
is_production=False,
credential_resolver=credential_resolver,
)
order_manager=sdk.get_order_manager()
event_manager=sdk.get_event_manager()
# Build your OrderRequest here, for example:# from multisafepay.api.paths.orders.request.order_request import OrderRequest# order_request = OrderRequest(...)create_response=order_manager.create(
request_order=order_request,
terminal_group_id="<terminal_group_id>",
)
order=create_response.get_data()
withevent_manager.subscribe_order_events(order, timeout=45.0) asstream:
foreventinstream:
print(event)

Cloud POS order tips

For POS and Cloud POS integrations, you can send tip information as part of the order creation payload with amount_details. Amount values are expressed in the smallest currency unit, so this example sends a total order amount of EUR 1.20 with EUR 0.20 marked as tip.

frommultisafepay.api.paths.orders.requestimportOrderRequestfrommultisafepay.api.paths.orders.request.componentsimport (
AmountDetails,
Tip,
)
order_request= (
OrderRequest()
.add_type("redirect")
.add_order_id("cloud-pos-order-with-tip")
.add_description("Cloud POS order with tip")
.add_amount(120)
.add_currency("EUR")
.add_gateway_info({"terminal_id": "<terminal_id>"})
.add_amount_details(
AmountDetails().add_tip(Tip().add_amount(20)),
)
)

The amount_details field serializes to:

{
"amount_details": {
"tip": {
"amount": 20
}
}
}

See the full Cloud POS tip example in examples/order_manager/cloud_pos_order_with_tip.py.

Development-only custom base URL override

By default, the SDK only targets:

  • test: https://testapi.multisafepay.com/v1/
  • live: https://api.multisafepay.com/v1/

For local development, a custom base URL can be enabled with strict guardrails:

export MSP_SDK_BUILD_PROFILE=dev
export MSP_SDK_ALLOW_CUSTOM_BASE_URL=1

You can provide the custom base URL either via environment variable or via the SDK argument.

Environment variable option:

export MSP_SDK_CUSTOM_BASE_URL="https://dev-api.example.com/v1"

SDK argument option:

frommultisafepayimportSdksdk=Sdk(
api_key="<api_key>",
is_production=False,
base_url="https://dev-api.example.com/v1",
)

Precedence when both are set:

  • The explicit SDK argument base_url takes priority.
  • If base_url is not passed, MSP_SDK_CUSTOM_BASE_URL is used.

In any non-dev profile (including default release), custom base URLs are blocked and the SDK will only use test/live URLs.

Examples

Go to the folder examples to see how to use the SDK.

The event-stream example in examples/event_manager/subscribe_events.py requires:

export API_KEY="<account_api_key>"export TERMINAL_GROUP_API_KEY_GROUP_DEFAULT="<terminal_group_api_key>"export CLOUD_POS_TERMINAL_GROUP_ID="<terminal_group_id>"export CLOUD_POS_TERMINAL_ID="<terminal_id>"

The SSE E2E test can also run against a dev-backed base URL and optionally resolve the terminal group automatically:

export E2E_NO_SANDBOX_BASE_URL="https://dev-api.example.com/v1/"export MSP_SDK_BUILD_PROFILE=dev
export MSP_SDK_ALLOW_CUSTOM_BASE_URL=1
export MSP_SDK_CUSTOM_BASE_URL="https://dev-api.example.com/v1/"export E2E_API_KEY="<account_api_key>"export E2E_TERMINAL_GROUP_API_KEY_GROUP_DEFAULT="<terminal_group_api_key>"export E2E_CLOUD_POS_TERMINAL_ID="<terminal_id>"# Optional when CLOUD_POS_TERMINAL_GROUP_ID is not setexport E2E_PARTNER_API_KEY="<partner_api_key>"

Code quality checks

Linting

make lint

Testing

make test

E2E target environment

By default, E2E tests target https://testapi.multisafepay.com/v1/.

Use dedicated E2E variables instead of the general SDK variables:

export E2E_API_KEY="<test_api_key>"export E2E_BASE_URL="https://testapi.multisafepay.com/v1/"# optional
make test-e2e

E2E_BASE_URL is optional and can point to any HTTPS base URL used for E2E. When omitted, E2E defaults to testapi.multisafepay.com.

The e2e suite does not use the shared API_KEY variable or the shared MSP_SDK_* custom base URL settings.

Terminal endpoint examples and E2E checks use a dev-backed base URL because those endpoints are not exercised against the default shared E2E target.

export API_KEY="<account_api_key>"export PARTNER_API_KEY="<partner_api_key>"# optionalexport MSP_SDK_BUILD_PROFILE=dev
export MSP_SDK_ALLOW_CUSTOM_BASE_URL=1
export MSP_SDK_CUSTOM_BASE_URL="https://dev-api.example.com/v1/"export E2E_CLOUD_POS_TERMINAL_ID="<terminal_id>"# Optional: set when you want to skip automatic terminal-group lookupexport CLOUD_POS_TERMINAL_GROUP_ID="<terminal_group_id>"
make test-e2e

Support

Create an issue on this repository or email integration@multisafepay.com

Contributors

If you create a pull request to suggest an improvement, we'll send you some MultiSafepay swag as a thank you!

License

Open Software License (OSL 3.0)

Want to be part of the team?

Are you a developer interested in working at MultiSafepay? Check out our job openings and feel free to get in touch!

About

The default Python library for connecting to the MultiSafepay REST API

Resources

Code of conduct

Contributing

Stars

0 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

MultiSafepay Python SDK

Code QualityCodecovLicenseLatest stable versionPython versions

Easily integrate MultiSafepay's payment solutions into your Python applications with this official API client. This SDK provides convenient access to the MultiSafepay REST API, supports all core payment features, and is designed for seamless integration into any Python-based backend.

About MultiSafepay

MultiSafepay is a Dutch payment services provider, which takes care of contracts, processing transactions, and collecting payment for a range of local and international payment methods. Start selling online today and manage all your transactions in one place!

Installation

If you want to use the built-in default transport, install with the requests extra.

pip install "multisafepay[requests]"

If you want to provide your own transport implementation, install the base package.

pip install multisafepay

HTTP client / transport (optional dependency)

WARNING: This SDK does not have a hard dependency on a specific HTTP client.

The SDK uses a small transport abstraction so you can choose (and swap) the underlying HTTP implementation without affecting the rest of your integration.

How it works

  • The SDK expects an object implementing the HTTPTransport / HTTPResponse protocols defined in src/multisafepay/transport/http_transport.py.
  • Event stream subscriptions additionally require the transport to implement the HTTPStreamingTransport protocol (adds open_stream(...) returning an HTTPStreamResponse with readline(), close(), and raise_for_status()).
  • If you do not provide a transport, the SDK defaults to RequestsTransport.
  • requests is an optional extra:
    • To use the default transport, install multisafepay[requests].
    • To avoid requests, inject your own transport (for example, httpx or urllib3).

The built-in RequestsTransport implements both HTTPTransport and HTTPStreamingTransport, so the same configured requests.Session is reused for regular requests and SSE streams. Custom transports that only implement HTTPTransport (request(...)) can still be used for regular API calls, but SSE subscriptions fail explicitly until they also implement HTTPStreamingTransport. The SDK does not fall back to another HTTP library for event streams.

Custom transport example

pip install multisafepay
frommultisafepayimportSdksdk=Sdk(
api_key="<api_key>",
is_production=False,
transport=my_custom_transport, # must implement HTTPTransport
)

See transport examples in examples/transport/ (httpx_transport.py, urllib3_transport.py, request_transport.py).

Getting started

Initialize the client

frommultisafepayimportSdkmultisafepay_sdk: Sdk=Sdk(api_key='<api_key>', is_production=True)

Initialize with scoped credentials

Use ScopedCredentialResolver when different API keys must be selected per auth scope. When credential_resolver is provided, api_key becomes optional.

frommultisafepayimportSdkfrommultisafepay.clientimportScopedCredentialResolvercredential_resolver=ScopedCredentialResolver(
default_api_key="<default_api_key>",
partner_affiliate_api_key="<partner_api_key>",
terminal_group_api_keys={
"<terminal_group_id>": "<terminal_group_api_key>",
},
)
sdk=Sdk(
is_production=False,
credential_resolver=credential_resolver,
)

Event stream subscriptions

Use EventManager to subscribe to MultiSafepay SSE streams directly, or to subscribe from an order response that already contains event credentials.

frommultisafepayimportSdkfrommultisafepay.clientimportScopedCredentialResolvercredential_resolver=ScopedCredentialResolver(
default_api_key="<default_api_key>",
terminal_group_api_keys={
"<terminal_group_id>": "<terminal_group_api_key>",
},
)
sdk=Sdk(
is_production=False,
credential_resolver=credential_resolver,
)
order_manager=sdk.get_order_manager()
event_manager=sdk.get_event_manager()
# Build your OrderRequest here, for example:# from multisafepay.api.paths.orders.request.order_request import OrderRequest# order_request = OrderRequest(...)create_response=order_manager.create(
request_order=order_request,
terminal_group_id="<terminal_group_id>",
)
order=create_response.get_data()
withevent_manager.subscribe_order_events(order, timeout=45.0) asstream:
foreventinstream:
print(event)

Cloud POS order tips

For POS and Cloud POS integrations, you can send tip information as part of the order creation payload with amount_details. Amount values are expressed in the smallest currency unit, so this example sends a total order amount of EUR 1.20 with EUR 0.20 marked as tip.

frommultisafepay.api.paths.orders.requestimportOrderRequestfrommultisafepay.api.paths.orders.request.componentsimport (
AmountDetails,
Tip,
)
order_request= (
OrderRequest()
.add_type("redirect")
.add_order_id("cloud-pos-order-with-tip")
.add_description("Cloud POS order with tip")
.add_amount(120)
.add_currency("EUR")
.add_gateway_info({"terminal_id": "<terminal_id>"})
.add_amount_details(
AmountDetails().add_tip(Tip().add_amount(20)),
)
)

The amount_details field serializes to:

{
"amount_details": {
"tip": {
"amount": 20
}
}
}

See the full Cloud POS tip example in examples/order_manager/cloud_pos_order_with_tip.py.

Development-only custom base URL override

By default, the SDK only targets:

  • test: https://testapi.multisafepay.com/v1/
  • live: https://api.multisafepay.com/v1/

For local development, a custom base URL can be enabled with strict guardrails:

export MSP_SDK_BUILD_PROFILE=dev
export MSP_SDK_ALLOW_CUSTOM_BASE_URL=1

You can provide the custom base URL either via environment variable or via the SDK argument.

Environment variable option:

export MSP_SDK_CUSTOM_BASE_URL="https://dev-api.example.com/v1"

SDK argument option:

frommultisafepayimportSdksdk=Sdk(
api_key="<api_key>",
is_production=False,
base_url="https://dev-api.example.com/v1",
)

Precedence when both are set:

  • The explicit SDK argument base_url takes priority.
  • If base_url is not passed, MSP_SDK_CUSTOM_BASE_URL is used.

In any non-dev profile (including default release), custom base URLs are blocked and the SDK will only use test/live URLs.

Examples

Go to the folder examples to see how to use the SDK.

The event-stream example in examples/event_manager/subscribe_events.py requires:

export API_KEY="<account_api_key>"export TERMINAL_GROUP_API_KEY_GROUP_DEFAULT="<terminal_group_api_key>"export CLOUD_POS_TERMINAL_GROUP_ID="<terminal_group_id>"export CLOUD_POS_TERMINAL_ID="<terminal_id>"

The SSE E2E test can also run against a dev-backed base URL and optionally resolve the terminal group automatically:

export E2E_NO_SANDBOX_BASE_URL="https://dev-api.example.com/v1/"export MSP_SDK_BUILD_PROFILE=dev
export MSP_SDK_ALLOW_CUSTOM_BASE_URL=1
export MSP_SDK_CUSTOM_BASE_URL="https://dev-api.example.com/v1/"export E2E_API_KEY="<account_api_key>"export E2E_TERMINAL_GROUP_API_KEY_GROUP_DEFAULT="<terminal_group_api_key>"export E2E_CLOUD_POS_TERMINAL_ID="<terminal_id>"# Optional when CLOUD_POS_TERMINAL_GROUP_ID is not setexport E2E_PARTNER_API_KEY="<partner_api_key>"

Code quality checks

Linting

make lint

Testing

make test

E2E target environment

By default, E2E tests target https://testapi.multisafepay.com/v1/.

Use dedicated E2E variables instead of the general SDK variables:

export E2E_API_KEY="<test_api_key>"export E2E_BASE_URL="https://testapi.multisafepay.com/v1/"# optional
make test-e2e

E2E_BASE_URL is optional and can point to any HTTPS base URL used for E2E. When omitted, E2E defaults to testapi.multisafepay.com.

The e2e suite does not use the shared API_KEY variable or the shared MSP_SDK_* custom base URL settings.

Terminal endpoint examples and E2E checks use a dev-backed base URL because those endpoints are not exercised against the default shared E2E target.

export API_KEY="<account_api_key>"export PARTNER_API_KEY="<partner_api_key>"# optionalexport MSP_SDK_BUILD_PROFILE=dev
export MSP_SDK_ALLOW_CUSTOM_BASE_URL=1
export MSP_SDK_CUSTOM_BASE_URL="https://dev-api.example.com/v1/"export E2E_CLOUD_POS_TERMINAL_ID="<terminal_id>"# Optional: set when you want to skip automatic terminal-group lookupexport CLOUD_POS_TERMINAL_GROUP_ID="<terminal_group_id>"
make test-e2e

Support

Create an issue on this repository or email integration@multisafepay.com

Contributors

If you create a pull request to suggest an improvement, we'll send you some MultiSafepay swag as a thank you!

License

Open Software License (OSL 3.0)

Want to be part of the team?

Are you a developer interested in working at MultiSafepay? Check out our job openings and feel free to get in touch!

About

The default Python library for connecting to the MultiSafepay REST API

Resources

Code of conduct

Contributing

Stars

0 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

MultiSafepay Python SDK

Code QualityCodecovLicenseLatest stable versionPython versions

Easily integrate MultiSafepay's payment solutions into your Python applications with this official API client. This SDK provides convenient access to the MultiSafepay REST API, supports all core payment features, and is designed for seamless integration into any Python-based backend.

About MultiSafepay

MultiSafepay is a Dutch payment services provider, which takes care of contracts, processing transactions, and collecting payment for a range of local and international payment methods. Start selling online today and manage all your transactions in one place!

Installation

If you want to use the built-in default transport, install with the requests extra.

pip install "multisafepay[requests]"

If you want to provide your own transport implementation, install the base package.

pip install multisafepay

HTTP client / transport (optional dependency)

WARNING: This SDK does not have a hard dependency on a specific HTTP client.

The SDK uses a small transport abstraction so you can choose (and swap) the underlying HTTP implementation without affecting the rest of your integration.

How it works

  • The SDK expects an object implementing the HTTPTransport / HTTPResponse protocols defined in src/multisafepay/transport/http_transport.py.
  • Event stream subscriptions additionally require the transport to implement the HTTPStreamingTransport protocol (adds open_stream(...) returning an HTTPStreamResponse with readline(), close(), and raise_for_status()).
  • If you do not provide a transport, the SDK defaults to RequestsTransport.
  • requests is an optional extra:
    • To use the default transport, install multisafepay[requests].
    • To avoid requests, inject your own transport (for example, httpx or urllib3).

The built-in RequestsTransport implements both HTTPTransport and HTTPStreamingTransport, so the same configured requests.Session is reused for regular requests and SSE streams. Custom transports that only implement HTTPTransport (request(...)) can still be used for regular API calls, but SSE subscriptions fail explicitly until they also implement HTTPStreamingTransport. The SDK does not fall back to another HTTP library for event streams.

Custom transport example

pip install multisafepay
frommultisafepayimportSdksdk=Sdk(
api_key="<api_key>",
is_production=False,
transport=my_custom_transport, # must implement HTTPTransport
)

See transport examples in examples/transport/ (httpx_transport.py, urllib3_transport.py, request_transport.py).

Getting started

Initialize the client

frommultisafepayimportSdkmultisafepay_sdk: Sdk=Sdk(api_key='<api_key>', is_production=True)

Initialize with scoped credentials

Use ScopedCredentialResolver when different API keys must be selected per auth scope. When credential_resolver is provided, api_key becomes optional.

frommultisafepayimportSdkfrommultisafepay.clientimportScopedCredentialResolvercredential_resolver=ScopedCredentialResolver(
default_api_key="<default_api_key>",
partner_affiliate_api_key="<partner_api_key>",
terminal_group_api_keys={
"<terminal_group_id>": "<terminal_group_api_key>",
},
)
sdk=Sdk(
is_production=False,
credential_resolver=credential_resolver,
)

Event stream subscriptions

Use EventManager to subscribe to MultiSafepay SSE streams directly, or to subscribe from an order response that already contains event credentials.

frommultisafepayimportSdkfrommultisafepay.clientimportScopedCredentialResolvercredential_resolver=ScopedCredentialResolver(
default_api_key="<default_api_key>",
terminal_group_api_keys={
"<terminal_group_id>": "<terminal_group_api_key>",
},
)
sdk=Sdk(
is_production=False,
credential_resolver=credential_resolver,
)
order_manager=sdk.get_order_manager()
event_manager=sdk.get_event_manager()
# Build your OrderRequest here, for example:# from multisafepay.api.paths.orders.request.order_request import OrderRequest# order_request = OrderRequest(...)create_response=order_manager.create(
request_order=order_request,
terminal_group_id="<terminal_group_id>",
)
order=create_response.get_data()
withevent_manager.subscribe_order_events(order, timeout=45.0) asstream:
foreventinstream:
print(event)

Cloud POS order tips

For POS and Cloud POS integrations, you can send tip information as part of the order creation payload with amount_details. Amount values are expressed in the smallest currency unit, so this example sends a total order amount of EUR 1.20 with EUR 0.20 marked as tip.

frommultisafepay.api.paths.orders.requestimportOrderRequestfrommultisafepay.api.paths.orders.request.componentsimport (
AmountDetails,
Tip,
)
order_request= (
OrderRequest()
.add_type("redirect")
.add_order_id("cloud-pos-order-with-tip")
.add_description("Cloud POS order with tip")
.add_amount(120)
.add_currency("EUR")
.add_gateway_info({"terminal_id": "<terminal_id>"})
.add_amount_details(
AmountDetails().add_tip(Tip().add_amount(20)),
)
)

The amount_details field serializes to:

{
"amount_details": {
"tip": {
"amount": 20
}
}
}

See the full Cloud POS tip example in examples/order_manager/cloud_pos_order_with_tip.py.

Development-only custom base URL override

By default, the SDK only targets:

  • test: https://testapi.multisafepay.com/v1/
  • live: https://api.multisafepay.com/v1/

For local development, a custom base URL can be enabled with strict guardrails:

export MSP_SDK_BUILD_PROFILE=dev
export MSP_SDK_ALLOW_CUSTOM_BASE_URL=1

You can provide the custom base URL either via environment variable or via the SDK argument.

Environment variable option:

export MSP_SDK_CUSTOM_BASE_URL="https://dev-api.example.com/v1"

SDK argument option:

frommultisafepayimportSdksdk=Sdk(
api_key="<api_key>",
is_production=False,
base_url="https://dev-api.example.com/v1",
)

Precedence when both are set:

  • The explicit SDK argument base_url takes priority.
  • If base_url is not passed, MSP_SDK_CUSTOM_BASE_URL is used.

In any non-dev profile (including default release), custom base URLs are blocked and the SDK will only use test/live URLs.

Examples

Go to the folder examples to see how to use the SDK.

The event-stream example in examples/event_manager/subscribe_events.py requires:

export API_KEY="<account_api_key>"export TERMINAL_GROUP_API_KEY_GROUP_DEFAULT="<terminal_group_api_key>"export CLOUD_POS_TERMINAL_GROUP_ID="<terminal_group_id>"export CLOUD_POS_TERMINAL_ID="<terminal_id>"

The SSE E2E test can also run against a dev-backed base URL and optionally resolve the terminal group automatically:

export E2E_NO_SANDBOX_BASE_URL="https://dev-api.example.com/v1/"export MSP_SDK_BUILD_PROFILE=dev
export MSP_SDK_ALLOW_CUSTOM_BASE_URL=1
export MSP_SDK_CUSTOM_BASE_URL="https://dev-api.example.com/v1/"export E2E_API_KEY="<account_api_key>"export E2E_TERMINAL_GROUP_API_KEY_GROUP_DEFAULT="<terminal_group_api_key>"export E2E_CLOUD_POS_TERMINAL_ID="<terminal_id>"# Optional when CLOUD_POS_TERMINAL_GROUP_ID is not setexport E2E_PARTNER_API_KEY="<partner_api_key>"

Code quality checks

Linting

make lint

Testing

make test

E2E target environment

By default, E2E tests target https://testapi.multisafepay.com/v1/.

Use dedicated E2E variables instead of the general SDK variables:

export E2E_API_KEY="<test_api_key>"export E2E_BASE_URL="https://testapi.multisafepay.com/v1/"# optional
make test-e2e

E2E_BASE_URL is optional and can point to any HTTPS base URL used for E2E. When omitted, E2E defaults to testapi.multisafepay.com.

The e2e suite does not use the shared API_KEY variable or the shared MSP_SDK_* custom base URL settings.

Terminal endpoint examples and E2E checks use a dev-backed base URL because those endpoints are not exercised against the default shared E2E target.

export API_KEY="<account_api_key>"export PARTNER_API_KEY="<partner_api_key>"# optionalexport MSP_SDK_BUILD_PROFILE=dev
export MSP_SDK_ALLOW_CUSTOM_BASE_URL=1
export MSP_SDK_CUSTOM_BASE_URL="https://dev-api.example.com/v1/"export E2E_CLOUD_POS_TERMINAL_ID="<terminal_id>"# Optional: set when you want to skip automatic terminal-group lookupexport CLOUD_POS_TERMINAL_GROUP_ID="<terminal_group_id>"
make test-e2e

Support

Create an issue on this repository or email integration@multisafepay.com

Contributors

If you create a pull request to suggest an improvement, we'll send you some MultiSafepay swag as a thank you!

License

Open Software License (OSL 3.0)

Want to be part of the team?

Are you a developer interested in working at MultiSafepay? Check out our job openings and feel free to get in touch!

About

The default Python library for connecting to the MultiSafepay REST API

Resources

Code of conduct

Contributing

Stars

0 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

MultiSafepay Python SDK

Code QualityCodecovLicenseLatest stable versionPython versions

Easily integrate MultiSafepay's payment solutions into your Python applications with this official API client. This SDK provides convenient access to the MultiSafepay REST API, supports all core payment features, and is designed for seamless integration into any Python-based backend.

About MultiSafepay

MultiSafepay is a Dutch payment services provider, which takes care of contracts, processing transactions, and collecting payment for a range of local and international payment methods. Start selling online today and manage all your transactions in one place!

Installation

If you want to use the built-in default transport, install with the requests extra.

pip install "multisafepay[requests]"

If you want to provide your own transport implementation, install the base package.

pip install multisafepay

HTTP client / transport (optional dependency)

WARNING: This SDK does not have a hard dependency on a specific HTTP client.

The SDK uses a small transport abstraction so you can choose (and swap) the underlying HTTP implementation without affecting the rest of your integration.

How it works

  • The SDK expects an object implementing the HTTPTransport / HTTPResponse protocols defined in src/multisafepay/transport/http_transport.py.
  • Event stream subscriptions additionally require the transport to implement the HTTPStreamingTransport protocol (adds open_stream(...) returning an HTTPStreamResponse with readline(), close(), and raise_for_status()).
  • If you do not provide a transport, the SDK defaults to RequestsTransport.
  • requests is an optional extra:
    • To use the default transport, install multisafepay[requests].
    • To avoid requests, inject your own transport (for example, httpx or urllib3).

The built-in RequestsTransport implements both HTTPTransport and HTTPStreamingTransport, so the same configured requests.Session is reused for regular requests and SSE streams. Custom transports that only implement HTTPTransport (request(...)) can still be used for regular API calls, but SSE subscriptions fail explicitly until they also implement HTTPStreamingTransport. The SDK does not fall back to another HTTP library for event streams.

Custom transport example

pip install multisafepay
frommultisafepayimportSdksdk=Sdk(
api_key="<api_key>",
is_production=False,
transport=my_custom_transport, # must implement HTTPTransport
)

See transport examples in examples/transport/ (httpx_transport.py, urllib3_transport.py, request_transport.py).

Getting started

Initialize the client

frommultisafepayimportSdkmultisafepay_sdk: Sdk=Sdk(api_key='<api_key>', is_production=True)

Initialize with scoped credentials

Use ScopedCredentialResolver when different API keys must be selected per auth scope. When credential_resolver is provided, api_key becomes optional.

frommultisafepayimportSdkfrommultisafepay.clientimportScopedCredentialResolvercredential_resolver=ScopedCredentialResolver(
default_api_key="<default_api_key>",
partner_affiliate_api_key="<partner_api_key>",
terminal_group_api_keys={
"<terminal_group_id>": "<terminal_group_api_key>",
},
)
sdk=Sdk(
is_production=False,
credential_resolver=credential_resolver,
)

Event stream subscriptions

Use EventManager to subscribe to MultiSafepay SSE streams directly, or to subscribe from an order response that already contains event credentials.

frommultisafepayimportSdkfrommultisafepay.clientimportScopedCredentialResolvercredential_resolver=ScopedCredentialResolver(
default_api_key="<default_api_key>",
terminal_group_api_keys={
"<terminal_group_id>": "<terminal_group_api_key>",
},
)
sdk=Sdk(
is_production=False,
credential_resolver=credential_resolver,
)
order_manager=sdk.get_order_manager()
event_manager=sdk.get_event_manager()
# Build your OrderRequest here, for example:# from multisafepay.api.paths.orders.request.order_request import OrderRequest# order_request = OrderRequest(...)create_response=order_manager.create(
request_order=order_request,
terminal_group_id="<terminal_group_id>",
)
order=create_response.get_data()
withevent_manager.subscribe_order_events(order, timeout=45.0) asstream:
foreventinstream:
print(event)

Cloud POS order tips

For POS and Cloud POS integrations, you can send tip information as part of the order creation payload with amount_details. Amount values are expressed in the smallest currency unit, so this example sends a total order amount of EUR 1.20 with EUR 0.20 marked as tip.

frommultisafepay.api.paths.orders.requestimportOrderRequestfrommultisafepay.api.paths.orders.request.componentsimport (
AmountDetails,
Tip,
)
order_request= (
OrderRequest()
.add_type("redirect")
.add_order_id("cloud-pos-order-with-tip")
.add_description("Cloud POS order with tip")
.add_amount(120)
.add_currency("EUR")
.add_gateway_info({"terminal_id": "<terminal_id>"})
.add_amount_details(
AmountDetails().add_tip(Tip().add_amount(20)),
)
)

The amount_details field serializes to:

{
"amount_details": {
"tip": {
"amount": 20
}
}
}

See the full Cloud POS tip example in examples/order_manager/cloud_pos_order_with_tip.py.

Development-only custom base URL override

By default, the SDK only targets:

  • test: https://testapi.multisafepay.com/v1/
  • live: https://api.multisafepay.com/v1/

For local development, a custom base URL can be enabled with strict guardrails:

export MSP_SDK_BUILD_PROFILE=dev
export MSP_SDK_ALLOW_CUSTOM_BASE_URL=1

You can provide the custom base URL either via environment variable or via the SDK argument.

Environment variable option:

export MSP_SDK_CUSTOM_BASE_URL="https://dev-api.example.com/v1"

SDK argument option:

frommultisafepayimportSdksdk=Sdk(
api_key="<api_key>",
is_production=False,
base_url="https://dev-api.example.com/v1",
)

Precedence when both are set:

  • The explicit SDK argument base_url takes priority.
  • If base_url is not passed, MSP_SDK_CUSTOM_BASE_URL is used.

In any non-dev profile (including default release), custom base URLs are blocked and the SDK will only use test/live URLs.

Examples

Go to the folder examples to see how to use the SDK.

The event-stream example in examples/event_manager/subscribe_events.py requires:

export API_KEY="<account_api_key>"export TERMINAL_GROUP_API_KEY_GROUP_DEFAULT="<terminal_group_api_key>"export CLOUD_POS_TERMINAL_GROUP_ID="<terminal_group_id>"export CLOUD_POS_TERMINAL_ID="<terminal_id>"

The SSE E2E test can also run against a dev-backed base URL and optionally resolve the terminal group automatically:

export E2E_NO_SANDBOX_BASE_URL="https://dev-api.example.com/v1/"export MSP_SDK_BUILD_PROFILE=dev
export MSP_SDK_ALLOW_CUSTOM_BASE_URL=1
export MSP_SDK_CUSTOM_BASE_URL="https://dev-api.example.com/v1/"export E2E_API_KEY="<account_api_key>"export E2E_TERMINAL_GROUP_API_KEY_GROUP_DEFAULT="<terminal_group_api_key>"export E2E_CLOUD_POS_TERMINAL_ID="<terminal_id>"# Optional when CLOUD_POS_TERMINAL_GROUP_ID is not setexport E2E_PARTNER_API_KEY="<partner_api_key>"

Code quality checks

Linting

make lint

Testing

make test

E2E target environment

By default, E2E tests target https://testapi.multisafepay.com/v1/.

Use dedicated E2E variables instead of the general SDK variables:

export E2E_API_KEY="<test_api_key>"export E2E_BASE_URL="https://testapi.multisafepay.com/v1/"# optional
make test-e2e

E2E_BASE_URL is optional and can point to any HTTPS base URL used for E2E. When omitted, E2E defaults to testapi.multisafepay.com.

The e2e suite does not use the shared API_KEY variable or the shared MSP_SDK_* custom base URL settings.

Terminal endpoint examples and E2E checks use a dev-backed base URL because those endpoints are not exercised against the default shared E2E target.

export API_KEY="<account_api_key>"export PARTNER_API_KEY="<partner_api_key>"# optionalexport MSP_SDK_BUILD_PROFILE=dev
export MSP_SDK_ALLOW_CUSTOM_BASE_URL=1
export MSP_SDK_CUSTOM_BASE_URL="https://dev-api.example.com/v1/"export E2E_CLOUD_POS_TERMINAL_ID="<terminal_id>"# Optional: set when you want to skip automatic terminal-group lookupexport CLOUD_POS_TERMINAL_GROUP_ID="<terminal_group_id>"
make test-e2e

Support

Create an issue on this repository or email integration@multisafepay.com

Contributors

If you create a pull request to suggest an improvement, we'll send you some MultiSafepay swag as a thank you!

License

Open Software License (OSL 3.0)

Want to be part of the team?

Are you a developer interested in working at MultiSafepay? Check out our job openings and feel free to get in touch!

About

The default Python library for connecting to the MultiSafepay REST API

Resources

Code of conduct

Contributing

Stars

0 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages