The payment object

Recipes
✅
Accept a payment and confirm it with a webhook
Open Recipe
🔁
Bill a customer every month
Open Recipe

You can expect the below object when you register a webhook for the following events:

  • payment.failed
  • payment.succeeded
  • payment.refund_initiated
  • payment.refunded
  • payment.refund_failed
  • payment.card_verified
  • payment.authorized
  • payment.voided

Raw sample

{
  "status": "captured",
  "id": "PAY-3C4D5E6F7A",
  "amount": 33.99,
  "amount_currency": "AED",
  "refund_amount": 0,
  "refund_status": "No refund",
  "refunds": [],
  "max_refund_amount": 33.99,
  "billing_descriptor": "Mamo*Merchant",
  "custom_data": {},
  "created_date": "2023-12-25-14-38-53",
  "subscription_id": null,
  "settlement_amount": "31.99",
  "settlement_currency": "AED",
  "settlement_date": "2024-01-01",
  "customer_details": {
    "name": "",
    "email": "[email protected]",
    "phone_number": "-",
    "comment": "-"
  },
  "payment_method": {
    "card_id": null,
    "method": "Card",
    "type": "CREDIT VISA",
    "card_holder_name": "John Doe",
    "card_last4": "1111",
    "card_expiry_month": "12",
    "card_expiry_year": "2028",
    "origin": "International card"
  },
  "settlement_fee": "AED 1.90",
  "settlement_vat": "AED 0.10",
  "payment_link_id": "MB-LINK-5E6F7A8B9C",
  "payment_link_url": "https://sandbox.dev.business.mamopay.com/pay/acme-chocolates-4d5e6f",
  "external_id": null,
  "error_code": null,
  "error_message": null,
  "next_payment_date": null,
  "event_type": "payment.succeeded"
}

Field descriptions

FieldTypeDescription
statusStringStatus of the payment: confirmation_required, processing, authorized, card_verified, captured, failed or voided. Refunds don't change status: a refunded payment stays captured. refund_initiated and refunded are legacy values that only payments created before February 2022 can return. See the lifecycle below.
redirect_urlStringURL the customer must visit to complete authentication. Present only while status is confirmation_required, so it never appears on webhooks
idStringMamo payment ID
amountNumberAmount
amount_currencyStringCurrency
refund_amountNumberTotal amount refunded on this payment so far. 0 if none
refund_statusStringRefund state: initiated, success, failed, or No refund (no refund, or one still pending)
refundsArrayBreakdown of each individual refund issued (id-amount-amount_currency-billing_amount-billing_amount_currency)
max_refund_amountNumberThe maximum amount that can still be refunded on this payment
custom_dataDictionaryCustom Data sent upon creation
created_dateString (Format: YYYY-MM-DD-HH-MM-SS)payment creation time
subscription_idStringSubscription ID (in case of a subscription payment)
settlement_amountStringAmount settled to you, without currency, formatted with thousands separators (for example "1,356.42"). "0.00" until settled
settlement_currencyStringSettlement Currency
settlement_dateString (Format: YYYY-MM-DD)Expected settlement date, or - until a settlement is scheduled. Never null
customer_detailsDictionaryCustomer Details (name-email-phone_number-comment). Each value is - when unknown
external_idStringExternal ID if sent
payment_methodObjectPayment Method (card_id-method-type-card_holder_name-card_last4-card_expiry_month-card_expiry_year-origin). method is Card, Apple Pay, Google Pay or Recurring; type is Tabby, or the card type and brand upper-cased, for example CREDIT VISA
settlement_feeStringMamo fee with currency, for example AED 1.90. Never null
settlement_vatStringVAT on the Mamo fee with currency, for example AED 0.10. Never null
payment_link_idStringID of payment link used
payment_link_urlStringPayment Link Used
error_codeStringError Code (In case of failure)
error_messageStringError Message (In case of failure)
next_payment_dateString (Format: DD/MM/YYYY)Next renewal date for subscription payments; null otherwise
event_typeStringCorresponding Webhook Event

Payment status lifecycle

The status field tells you where a payment is in its lifecycle and, crucially, whether money has actually been received. On webhooks the status is reported raw — it is never remapped — so an authorized hold arrives as authorized (not captured) and a payment voided by Mamo's fraud screening arrives as voided (not failed). Note that confirmation_required and processing do not trigger a webhook, so you will not receive this object for those states; a payment you hear about via webhook has reached captured, failed, authorized, card_verified, or voided.

Refunds don't change status: a refunded payment stays captured, so you can refund it again up to max_refund_amount. Track refunds with refund_status (initiated, success, failed, or No refund), refund_amount, max_refund_amount and refunds[]. Payments made before February 2022 may still show the legacy values refund_initiated or refunded.

StatusFunds received?Meaning
confirmation_requiredNoInitial state. The payment is waiting on the customer to complete authentication / confirmation (for example 3-D Secure or a redirect). No money has moved. Does not trigger a webhook, so it does not arrive via this object.
processingNo (pending)The payment has been submitted to the gateway and the outcome is not yet known. Do not treat this as success. Does not trigger a webhook, so it does not arrive via this object.
authorizedHeld, not capturedFunds have been authorized (placed on hold on the customer's card) but not captured. This happens on hold_and_charge_later links, and on future-dated subscription links — see the note below.
capturedYesThe payment succeeded and funds were captured. This is the terminal success state and the amount will be settled to you.
card_verifiedNo (no charge)The card was successfully verified (for example a card-saving or verification flow). No amount was captured.
failedNoThe payment attempt failed. Inspect error_code / error_message for the reason.
voidedNo (hold released)An authorization that was released before capture: either you released a hold with Reverse payment, or Mamo's fraud screening declined the payment after the card authorized it. Only the fraud-screening void sends a payment.voided webhook (with the raw voided status, never remapped to failed); releasing a hold yourself sends no webhook.
📘

Authorize vs. capture (hold_and_charge_later)

On a normal payment link the charge is captured immediately, so a successful payment goes straight to captured and the funds are received.

On a payment link configured with hold_and_charge_later, the payment is only authorized first: the amount is placed on hold on the customer's card and the charge sits in authorized until you capture it later. You can release the hold with Reverse payment, which moves the charge to voided; no webhook is sent for that, so use the Reverse Payment response as your confirmation. On webhooks these states are reported raw — an authorized hold is sent as authorized (not captured) — so rely on the status together with the link configuration to know whether a given payment is a hold or a completed capture.

What differs per event

The raw sample above is a payment.succeeded payload. Other events carry the same object shape — only these fields change:

EventstatusNotable fields
payment.failedfailederror_code / error_message populated; settlement_date is the placeholder string "-" and settlement_amount is the string "0.00" — these fields are always present, never actually null
payment.authorizedauthorizedSame settlement_date/settlement_amount placeholders as above until captured
payment.voidedvoidedSame as authorized, but Mamo's fraud screening voided the authorization — no money was taken and no further action is possible on this payment. Not sent when you release a hold with Reverse Payment
payment.card_verifiedcard_verifiedamount reflects the verification amount (if any); settlement_* fields are still present with the same placeholder values, since no revenue has been recorded
payment.refund_initiatedcapturedrefund_status: "initiated", refund_amount > 0, refunds has at least one entry, max_refund_amount reduced accordingly
payment.refundedcapturedrefund_status: "success". Two partial refunds send two payment.refunded events with the same id; tell them apart by refunds[].id
payment.refund_failedcapturedrefund_status: "failed"
📘

Marketplace / split payments

This object does not include payouts_share — the split amount is available on the payment link object instead. For a split payment, the recipient's share is credited to their recipient ledger on the payment's settlement date (at capture if your account settles same-day); it is paid out to their bank account later on a schedule (by default monthly). Use Fetch recipient balance to check a recipient's accrued ledger balance.