Skip to main content

getpayin

CI PyPI Python License

Official server-side Python SDK for the GetPayIn payment integration API. It wraps every integration endpoint with an idiomatic, typed API and computes the order-sensitive HMAC-SHA256 signatures for you, so you never have to build them by hand.

  • Checkouts (invoices.create)
  • Payment operations (payments.void / refund / settle / reverse_authorization / check_status)
  • Server-to-server card charges (vcc.charge)
  • Card tokenization (cards.tokenize / charge / revoke)
  • Recurring mandates (recurring.create / status / cancel / pause / resume)
  • Webhook signature verification (webhooks.verify)

Server-side only. Signing uses your secret hash_token. Never ship it to a browser or mobile client.

Requirements

  • Python 3.8+
  • Zero runtime dependencies — the default transport is built on the standard library

Install

pip install getpayin

Quick start

import os
from getpayin import GetpayinClient

getpayin = GetpayinClient(
    public_token=os.environ["GETPAYIN_PUBLIC_TOKEN"],
    hash_token=os.environ["GETPAYIN_HASH_TOKEN"],  # secret — server-side only
    # base_url defaults to https://pay.getpayin.com
    # timeout defaults to 30.0 seconds (per attempt)
    # max_retries defaults to 2 (set 0 to disable retries)
)

checkout = getpayin.invoices.create(
    first_name="John",
    last_name="Doe",
    email="john@example.com",
    order_title="Gold Plan",
    order_amount="250.00",  # pass amounts as strings to control the exact wire form
    currency="USD",
    redirection_url="https://shop.example.com/return",
    webhook_url="https://shop.example.com/webhooks/getpayin",
)

# Redirect the payer to the hosted checkout:
print(checkout.checkout_url, checkout.invoice_id, checkout.expires_at)

For an embedded checkout you can render inside your own page, pass iframe=True (sent in the request body but excluded from the signature, like payment_mode):

checkout = getpayin.invoices.create(
    first_name="John",
    last_name="Doe",
    email="john@example.com",
    order_title="Gold Plan",
    order_amount="250.00",
    currency="USD",
    iframe=True,  # enable embedded/iframe checkout
)

Then embed the returned checkout URL on your page. The <iframe> needs allow="payment *" so Apple Pay and Google Pay work inside the frame, and your page must listen for the completion message — in iframe mode the checkout signals the parent via postMessage instead of redirecting:

<iframe src="CHECKOUT_URL" allow="payment *"
        style="width:100%;min-height:640px;border:0" title="Secure checkout"></iframe>
<script>
  addEventListener('message', function (e) {
    if (e.origin !== 'https://pay.getpayin.com') return; // your API base origin
    if (!e.data || e.data.type !== 'getpayin_payment') return;
    window.location.href = e.data.success ? '/thank-you' : '/checkout?failed=1';
  });
</script>

The embedding page's origin must exactly match your integration's registered Origin, or the browser blocks framing and the message never arrives.

Both credentials are issued in the GetPayIn dashboard under Settings → Payment Integrations. public_token is sent on every request; hash_token is the secret used only to sign — it never leaves your server.

Payment operations

getpayin.payments.void(invoice_id=123)
getpayin.payments.settle(invoice_id=123, amount="50.00")
getpayin.payments.reverse_authorization(invoice_id=123)

status = getpayin.payments.check_status(invoice_id=123)
# PaymentResult(invoice_id=123, paid_status='PAID', auth_code='...')

# Refunds are idempotent when you pass an idempotency key — safe to retry:
refund = getpayin.payments.refund(
    invoice_id=123, amount="10.50", idempotency_key="refund-order-1234"
)
# RefundResult(..., refund_amount=10.5)

Card tokenization

result = getpayin.cards.tokenize(
    first_name="Jane", last_name="Doe",
    card_number="4111111111111111",
    card_expiry_month="12", card_expiry_year="2030", card_cvv="123",
    country="EG", address="1 Main St", city="Cairo",
)

getpayin.cards.charge(
    card_token=result.token,
    initiator="merchant",
    first_name="Jane", last_name="Doe",
    currency="USD", price="100.00", product="Monthly rebill",
    country="EG", address="1 Main St", city="Cairo",
)

getpayin.cards.revoke(card_token=result.token)

For US and CA billing addresses, also pass the state fields the API requires: us_state + postal_code (US) or canada_state + postal_code (CA).

Recurring mandates

mandate = getpayin.recurring.create(
    first_name="Sam", last_name="Doe", email="sam@example.com",
    order_title="Gold subscription",
    order_amount="250.00", currency="USD",
    cadence_interval="month", cadence_count=1, total_cycles=12,
    consent_text="I authorise recurring monthly charges.",
    idempotency_key="sub-signup-42",
)

getpayin.recurring.status(mandate.mandate_id)
getpayin.recurring.pause(mandate.mandate_id)
getpayin.recurring.resume(mandate.mandate_id)
getpayin.recurring.cancel(mandate.mandate_id)

Idempotency

Retrying a write after a network error or timeout risks performing it twice. To make that safe, pass an idempotency_key — the SDK sends it as the Idempotency-Key header and the server returns the original result instead of charging, refunding, or creating a second time. Keys are scoped per integration and capped at 64 characters.

Method A replay with the same key returns
invoices.create the original invoice and checkout_url
vcc.charge the original charge
cards.charge the original charge
payments.refund the original refund
recurring.create the original mandate

Reusing a key with a different request — for example recurring.create with changed terms, or payments.refund for a different amount — is rejected as a conflict: a GetpayinApiError with is_idempotency_conflict set (HTTP 409). Only the methods above honor the header.

Verifying webhooks

Pass the parsed body (a dict) or the raw JSON string/bytes to verify. It recomputes the signature with your hash_token and compares in constant time.

from flask import Flask, request, abort
from getpayin import GetpayinClient, GetpayinSignatureError

getpayin = GetpayinClient(public_token=..., hash_token=...)
app = Flask(__name__)

@app.post("/webhooks/getpayin")
def webhook():
    try:
        event = getpayin.webhooks.verify(request.get_data(as_text=True))
    except GetpayinSignatureError:
        abort(400)
    # event.event, event.invoice_id, event.success, event.raw, ...
    return "", 200

GetPayIn webhook signatures carry no timestamp, so verification does not protect against replay. Pair it with your own idempotency keyed on invoice_id.

Error handling

Every failure is a subclass of GetpayinError:

Error When
GetpayinConfigError Invalid client configuration (missing tokens, non-positive timeout).
GetpayinApiError The API returned an error. Carries status, errors, raw, retry_after_seconds, and the is_idempotency_conflict / is_rate_limited / is_forbidden flags.
GetpayinSignatureError A webhook signature did not verify.
GetpayinConnectionError Network failure or timeout (no HTTP response).
from getpayin import GetpayinApiError

try:
    getpayin.payments.refund(invoice_id=123, amount="10.00")
except GetpayinApiError as error:
    if error.is_idempotency_conflict:
        ...  # a refund with this idempotency key already exists

Retries and rate limiting

Every integration endpoint is rate limited server-side, so 429s are an expected condition under burst traffic. The SDK retries transient failures — 429, 5xx, connection errors, and timeouts — with exponential backoff and full jitter, honoring the server's Retry-After header when present.

A request is only ever replayed when replaying it cannot double-charge:

Replayed Not replayed
All GETs (recurring.status) vcc.charge, cards.charge, cards.tokenize
Any call you pass an idempotency_key to invoices.create, recurring.create without a key
payments.check_status (a pure read) recurring.cancel / pause / resume

So to make a refund safely retryable, pass an idempotency key — otherwise a failed refund surfaces immediately and is yours to handle. Tune or disable retries per client with max_retries (set 0 to turn them off). timeout applies to each attempt, so worst-case wall time is roughly (max_retries + 1) × timeout plus backoff. For requests the SDK will not replay, GetpayinApiError.retry_after_seconds exposes the server's backoff hint.

Amounts and precision

Signatures are computed over the exact bytes sent on the wire. To avoid any floating-point ambiguity, pass monetary amounts as strings (e.g. "10.50"). Numbers are accepted and stringified, but strings give you full control.

Custom transport

The default transport uses the standard library. Inject any callable matching getpayin.Transport — for a proxy-aware or connection-pooled HTTP client, or a mock in tests:

def transport(method, url, headers, body, timeout):
    resp = requests.request(method, url, headers=headers, data=body, timeout=timeout)
    return getpayin.HttpResponse(status=resp.status_code, text=resp.text, headers=dict(resp.headers))

getpayin = GetpayinClient(public_token=..., hash_token=..., transport=transport)

API reference

The full HTTP API — endpoints, fields, error codes, and test cards — is documented in the GetPayIn API reference: https://pay.getpayin.com/docs/payment_integration/index.html

Contributing

See CONTRIBUTING.md — in particular the note on signed-field ordering, which is the one thing that must stay in lockstep with the server.

Security issues: see SECURITY.md. Please do not open a public issue for a vulnerability.

License

MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

getpayin-0.2.0.tar.gz (33.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

getpayin-0.2.0-py3-none-any.whl (29.3 kB view details)

Uploaded Python 3

File details

Details for the file getpayin-0.2.0.tar.gz.

File metadata

  • Download URL: getpayin-0.2.0.tar.gz
  • Upload date:
  • Size: 33.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for getpayin-0.2.0.tar.gz
Algorithm Hash digest
SHA256 a4110d1828dbf1f8f2af50233cac324e3c8db60f2ece1b5b4b0cf60a03a97fe7
MD5 fefa2aea9cc413bb3a5bd175a9616ef3
BLAKE2b-256 48bdc307ff2e106924c419be2a3139a172a2f8ca9baa1ffd12b0690528915c86

See more details on using hashes here.

Provenance

The following attestation bundles were made for getpayin-0.2.0.tar.gz:

Publisher: release.yml on GetPayin-Tech/getpayin-python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file getpayin-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: getpayin-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 29.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for getpayin-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 a96eb8db1230caeb0ebc0eda7059b0351e89e44362c778d6b1d1b528a68a8e29
MD5 01d194951a2cef45201287fbdd7f7510
BLAKE2b-256 f9dee6691dd5c610df2550dda63c48e8af3ed4510b5611591223d7d706120a4a

See more details on using hashes here.

Provenance

The following attestation bundles were made for getpayin-0.2.0-py3-none-any.whl:

Publisher: release.yml on GetPayin-Tech/getpayin-python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page