MagicPayments Integration Guides

General Card H2H

Pay-inPay-outCards

Collect card data on your own form and post it server-to-server, then send card payouts. Requires PCI DSS scope.

When to use it

General Card H2H (host-to-host) is for merchants who collect card data on their own form and post it to us server-to-server. You own the checkout UI; we route the card to an acquirer and return the result. If 3-D Secure is required, we hand back a URL to send the cardholder to.

This path requires PCI DSS scope

Because raw PAN/CVV pass through your servers, your backend must be PCI DSS compliant and the call must be server-to-server over TLS — never from a browser or mobile app. If you don't want that scope, use the General Card Gate (hosted form) instead — the pay-out half of this page is identical to it.

Prerequisites

Signing credentials from Getting started and a cascade_id enabled for card pay-in (and one for card pay-out, which may be the same or different).

Pay-in — post card data directly

There is no invoice and no hosted page. You send the card and customer in one signed request.

  1. Collect card data on your form PAN, expiry month/year, cardholder name, and CVV.
  2. POST it server-to-server Call the card pay-in endpoint with your cascade_id.
  3. Handle 3-D Secure if needed If the response carries a payment_url, redirect the cardholder there to complete the challenge; otherwise the result is final.
  4. Receive the result Track via callback and/or the payment status endpoint.
POST/api/payment/payin/card
Request body
{
  "cascade_id": "card-h2h-eu",
  "payment": {
    "payment_id": "order-2026-000123",
    "currency": "USD",
    "amount": 2599,
    "description": "Order #98765 - Premium subscription"
  },
  "card": {
    "pan": "4111111111111111",
    "year": 2032,
    "month": 11,
    "card_holder": "JANE SUBSCRIBER",
    "cvv": "123"
  },
  "customer": {
    "id": "cust-4451",
    "full_name": "Jane Subscriber",
    "email": "[email protected]",
    "ip_address": "198.51.100.24"
  },
  "workflow_hooks": {
    "callback_url": "https://merchant.example.com/mp/callbacks",
    "return_success_url": "https://merchant.example.com/orders/98765/thank-you",
    "return_decline_url": "https://merchant.example.com/orders/98765/retry"
  }
}

Card field rules: cvv matches ^[0-9]{3,4}$; month is 1–12; year is the 4-digit year. Send the cardholder ip_address when you have it — it improves risk screening.

Python
resp = signed_post("/api/payment/payin/card", {
    "cascade_id": "card-h2h-eu",
    "payment": {
        "payment_id": "order-2026-000123",
        "currency": "USD",
        "amount": 2599,               # $25.99
        "description": "Order #98765 - Premium subscription",
    },
    "card": {
        "pan": "4111111111111111",
        "year": 2032,
        "month": 11,
        "card_holder": "JANE SUBSCRIBER",
        "cvv": "123",
    },
    "customer": {
        "id": "cust-4451",
        "full_name": "Jane Subscriber",
        "email": "[email protected]",
        "ip_address": "198.51.100.24",
    },
    "workflow_hooks": {
        "callback_url": "https://merchant.example.com/mp/callbacks",
        "return_success_url": "https://merchant.example.com/orders/98765/thank-you",
        "return_decline_url": "https://merchant.example.com/orders/98765/retry",
    },
})
payment = resp.json()
if payment.get("payment_url"):
    # 3-D Secure or other redirect step required — send the cardholder here.
    redirect_to(payment["payment_url"])
Response
{
  "request_status": "success",
  "payment_id": "4b77cec4-3f7a-4568-9b1c-7d2c4f1df7a2",
  "merchant_payment_id": "order-2026-000123",
  "payment_status": "processing",
  "payment_url": "https://stage.example-mp.com/public/payment/4b77cec4-.../3ds",
  "message": "Payment (PayIn) with internal uid=4b77cec4-... has been created.",
  "error_code": null,
  "error_message": null
}
Always check for payment_url

A non-null payment_url means a redirect step (typically 3-D Secure) is pending. Don't treat processing as failure — wait for the callback or poll status to reach a terminal state.

Tracking the pay-in

POST/api/payment/status
resp = signed_post("/api/payment/status", {
    "merchant_payment_id": "order-2026-000123"
})
payment = resp.json()
# payment["status"] -> "success" | "processing" | "decline" | "error"
# payment["processing_info"]["amount_acquired"] -> settled amount (minor units)

Pay-out — to a card

Card payouts are identical to the General Card Gate — the hosted form only affects pay-ins. The destination is the card number.

POST/api/payment/payout/card
Python
resp = signed_post("/api/payment/payout/card", {
    "cascade_id": "card-payout-eu",
    "payment": {
        "payment_id": "payout-2026-000045",
        "currency": "EUR",
        "amount": 15_000,             # €150.00
        "description": "Affiliate payout May 2026",
    },
    "card": {
        "pan": "5555555555554444",
        "year": 2032,
        "month": 11,
        "card_holder": "ACME AFFILIATES",
    },
    "customer": {"id": "partner-2391", "full_name": "Acme Affiliates OÜ"},
    "workflow_hooks": {"callback_url": "https://merchant.example.com/mp/callbacks"},
})
payout_uid = resp.json()["payment_id"]

Tracking the pay-out

Same as any payment — poll /api/payment/status by merchant_payment_id, or wait for the callback.

Testing & go-live