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
Raw sample
{
"status": "captured",
"id": "PAY-D65B203ABD",
"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,
"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-1EC52247EE",
"payment_link_url": "https://staging.business.mamopay.com/pay/mamosandbox-8acfdf",
"external_id": null,
"error_code": null,
"error_message": null,
"next_payment_date": null,
"event_type": "payment.succeeded"
}Field Descriptions
| Field | Type | Description |
|---|---|---|
| status | String | Status of the payment (confirmation_required-captured-refund_initiated-processing-failed-refunded) |
| id | String | Mamo payment ID |
| amount | Number | Amount |
| amount_currency | String | Currency |
| refund_amount | Number | Refund Amount (for a refund payment) |
| refund_status | String | Refund Status (for a refund payment) |
| refunds | Array | Breakdown of each individual refund issued (id-amount-amount_currency-billing_amount-billing_amount_currency) |
| max_refund_amount | Number | The maximum amount that can still be refunded on this payment |
| custom_data | Dictionary | Custom Data sent upon creation |
| created_date | String (Format: YYYY-MM-DD-HH-MM-SS) | payment creation time |
| subscription_id | String | Subscription ID (in case of a subscription payment) |
| settlement_amount | String | Settlement Amount |
| settlement_currency | String | Settlement Currency |
| settlement_date | String (Format: YYYY-MM-DD) | Settlement Date |
| customer_details | Dictionary | Customer Details (name-email-phone_number-comment) |
| external_id | String | External ID if sent |
| payment_method | String | Payment Method (card_id-type-card_holder_name-card_last4-card_expiry_month-card_expiry_year-origin) |
| settlement_fee | String | Settlement Fee |
| settlement_vat | String | Settlement VAT |
| payment_link_id | String | ID of payment link used |
| payment_link_url | String | Payment Link Used |
| error_code | String | Error Code (In case of failure) |
| error_message | String | Error Message (In case of failure) |
| next_payment_date | String | Next Payment Date (In case of failure) |
| event_type | String | Corresponding 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 released hold 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 (refund events carry refund_initiated / refunded).
| Status | Funds received? | Meaning |
|---|---|---|
confirmation_required | No | Initial 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. |
processing | No (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. |
authorized | Held, not captured | Funds 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. |
captured | Yes | The payment succeeded and funds were captured. This is the terminal success state and the amount will be settled to you. |
card_verified | No (no charge) | The card was successfully verified (for example a card-saving or verification flow). No amount was captured. |
refund_initiated | Yes, originally — refund pending | The payment was captured and a refund (full or partial) has been started but not yet completed. |
refunded | Returned to customer | The payment was captured and has since been refunded back to the customer. |
failed | No | The payment attempt failed. Inspect error_code / error_message for the reason. |
voided | No (hold released) | An authorization that was released/cancelled before capture. Always reported as voided on webhooks (the raw status is sent, so it is never remapped to failed). |
Authorize vs. capture (hold_and_charge_later)On a normal payment link the charge is captured immediately, so a successful payment goes straight to
capturedand 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 inauthorizeduntil you capture it later. The hold can be released, which moves the charge tovoided. On webhooks these states are reported raw — an authorized hold is sent asauthorized(notcaptured) and a released hold asvoided(notfailed) — so rely on thestatustogether 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:
| Event | status | Notable fields |
|---|---|---|
payment.failed | failed | error_code / error_message populated; settlement_date is the placeholder string "-" and settlement_amount reports 0 — these fields are always present, never actually null |
payment.authorized | authorized | Same settlement_date/settlement_amount placeholders as above until captured |
payment.voided | voided | Same as authorized, but the hold has been released — no further action possible on this payment |
payment.card_verified | card_verified | amount reflects the verification amount (if any); settlement_* fields are still present with the same placeholder values, since no revenue has been recorded |
payment.refund_initiated | refund_initiated | refund_amount > 0, refunds has at least one entry, max_refund_amount reduced accordingly |
payment.refunded | refunded | Same as refund_initiated, with the refund entry's state resolved |
Marketplace / split paymentsThis 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 when the payment is captured; 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.