DEEPayment SDK for Python
Python SDK for the merchant open API. Signing, digesting and body encryption are
handled by the SDK; merchants never assemble Signature-Input, Content-Digest
or the sealed box envelope themselves.
Protocol
protocol/ is the source of truth, shared with the Go / JavaScript /
PHP / Java SDKs through one set of test vectors:
| Item | Approach |
|---|---|
| Signature | Ed25519 + fixed RFC 9421 profile |
| Digest | RFC 9530 Content-Digest (sha-256) |
| POST body | X25519 sealed box envelope; the digest covers the sealed body |
| GET | No encryption, no body; only the public query and access key are signed |
| Webhook | Platform Ed25519 signature, plaintext body |
| Merchant identity | Located by Merchant-Access-Key only; signature params carry no keyId |
Installation
pip install deepayment
Runtime dependency: pynacl. Python 3.10 or newer.
Configuration
Six values; only the private key is generated on the merchant side, and the platform never receives it:
| Field | Meaning |
|---|---|
base_url |
HTTPS platform origin, without /api/v1 |
access_key |
Merchant access key issued by the platform |
merchant_private_key_base64 |
Merchant Ed25519 private key, kept on the merchant side only |
platform_body_key_id |
Current platform body key id (per deployment) |
platform_body_public_key_base64 |
Platform X25519 public key used to seal POST bodies |
platform_webhook_public_keys |
Platform webhook Ed25519 public keys, indexed by keyId |
Client rejects non-HTTPS base URLs. Synchronous responses remain plaintext JSON
and rely on HTTPS/TLS for confidentiality and integrity.
Quick start
from deepayment import APIError, Client, CreatePaymentReq, RequestError, ResponseError, TransportError
client = Client(
base_url="https://panama.deepayment.com", # production origin; no /api/v1
access_key="mak_live_xxx",
merchant_private_key_base64=merchant_private_key, # merchant Ed25519 private key, base64
platform_body_key_id="body_20260827_01",
platform_body_public_key_base64=platform_body_public_key, # platform X25519 public key, base64
platform_webhook_public_keys={ # keyId -> platform Ed25519 public key, base64
"pwhk_20260827_01": platform_webhook_public_key,
},
)
order = client.create_payment(CreatePaymentReq(
merchantOrderNo="M20260101001",
currency="BRL",
amount="100.00", # decimal string, never a float
paymentMethod={"code": "PIX", "pix": {"payerName": "Joao Silva"}},
webhookUrl="https://merchant.example/webhook/payments",
))
print(order.orderNo, order.status, order.action.url)
Two kinds of failure, opposite handling
| Error | Meaning | Handling |
|---|---|---|
RequestError |
Rejected before it was sent (local validation, a bad parameter, or a request the SDK could not encode or sign) | Safe to mark failed; fix the request and retry under the same merchantOrderNo |
TransportError |
Handed to the transport, no usable response (connection failure, timeout, interrupted read) | Outcome unknown; never mark a payout failed. Query by merchantOrderNo, or resend the identical request under the same number |
APIError |
The gateway returned a business error (msg / message / trace_id) |
Branch on msg. IDEMPOTENCY_CONFLICT: the number is already in flight, query it and keep querying rather than switching numbers. CHANNEL_ERROR: the order may already exist, query by merchantOrderNo first |
ResponseError |
The gateway or CDN returned something that is not an envelope (HTML 502, ...) | Outcome unknown; query before deciding |
ResponseTooLargeError |
A response arrived but exceeded the size limit and was discarded | Outcome unknown; the order was most likely created, query before deciding |
| Anything else | An unexpected error; assume the request may have arrived | Outcome unknown; query before deciding |
merchantOrderNo is the only key that prevents a duplicate order. A second
create with the same number never creates a second order: the platform answers
with the original order, or with IDEMPOTENCY_CONFLICT while the first one is
still being placed. The idempotency key travels with the request for tracing and
is not a deduplication key.
Two rules follow:
- After an unknown outcome, never allocate a new
merchantOrderNo. Query the existing one, or resend the same request under the same number. - A resend must carry identical parameters. The platform returns the original
order without comparing fields, so a changed amount or account silently has no
effect. To change anything, use a new
merchantOrderNoand reconcile the original order first.
The SDK validates locally before signing (top-level required fields and formats,
method shape and required extras); the rules are defined in
protocol/merchant-api.md.
Format checks (phone length, e-mail, ...) stay with the gateway on purpose so the
SDK cannot drift from it.
Next steps
Queries, idempotent retries and webhook verification are covered by the platform
documentation at https://docs.deepayment.com; its examples map one to one onto this
SDK. The wire protocol is in
protocol/webhook.md and
protocol/merchant-api.md. Key points:
- After a create timeout, query by
merchantOrderNofirst instead of sending a new order; a deliberate retry repeats the same call with the samemerchantOrderNo. - For webhooks, pass the method, path, headers and the raw, unparsed body bytes
to
client.parse_payment_webhook(method=, path=, headers=, body=); the SDK checks the digest, event id, time window and Ed25519 signature. Return 2xx once processed and deduplicate byeventId.
Amounts
Amounts, fees and rates in requests, responses, webhooks, balances, rates and
receipts are always decimal strings such as "100.00". Never use floats.
Order status
There are exactly six external statuses: PENDING PROCESSING SUCCEEDED
FAILED EXPIRED CANCELED.
Troubleshooting
| Situation | Action |
|---|---|
| Create request timed out | Query by merchantOrderNo; do not send a new order |
| Deliberate retry | Call the same method again with the same merchantOrderNo and identical parameters; the SDK regenerates the nonce on every request |
| Signature rejected | Check the server clock, the access key, and that the merchant private key matches the public key registered with the platform |
| Body decryption failed | Check that platform_body_key_id and the public key belong to the current environment |
| Webhook signature rejected | Check that the webhook key set contains the keyid from the header |
Tests
python -m pip install -e '.[dev]'
python -m pytest -q
The tests assert directly against the protocol/testdata
vectors: Signature-Input, the signature base and the signature value are compared
byte for byte, and body encryption is verified by opening ciphertext produced by
the reference implementation.
Release files for deepayment 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| deepayment-0.1.0.tar.gz | 34.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| deepayment-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 60.7 kB
Release files / deepayment-0.1.0.tar.gz
| Download URL | deepayment-0.1.0.tar.gz |
|---|---|
| Size | 34.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
95c08d7d64f3604feec3ffbfaef03f77aaf490224c49de6b01c2929067813f61
|
|
BLAKE2b-256 checksum How to use checksums |
cb35d81682042983880445eed96e1ea51ee6d50b15d9e82480bc2fb9211ecf60
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 14, 2026.
Transparency logRelease files / deepayment-0.1.0-py3-none-any.whl
| Download URL | deepayment-0.1.0-py3-none-any.whl |
|---|---|
| Size | 26.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
56c26f67ba74343dfe63afb2b4ddbc88fd4d9db7a66ec422e6d4cb98e9c8cae4
|
|
BLAKE2b-256 checksum How to use checksums |
cec3e7b202aa369e71d0702ab28f7798304a1a7451cec2ee3daf72b891c3dddd
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 14, 2026.
Transparency log