> For the complete documentation index, see [llms.txt](https://coda-payments.gitbook.io/coda-public-technical-documentation/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://coda-payments.gitbook.io/coda-public-technical-documentation/codapay/hosted-component-integration/cards/authorize-and-capture-a-payment-separately/auth-and-capture-notifications.md).

# Auth & Capture Notifications

This reference describes the webhook notifications specific to authorization and capture separation. These notifications keep your backend synchronized as a transaction moves through the authorize, capture, or cancel stages.

> **Note:** Authorization and capture separation requires POST notifications to be enabled. Both features must be activated for your account. Contact your Codapay account manager to enable them.

### Overview

Codapay uses webhooks to notify your "Complete Notification URL" whenever a transaction reaches a final state or a critical milestone in the authorization and capture flow.

> Ensure your "Complete Notification URL" is configured in the Publisher Portal. Refer to the [Set up payments](https://claude.ai/chat/cb09ca98-8996-43b7-87cb-cd20a04c1dea#) guide for detailed instructions.

### Event Types

For authorization and capture separation, your endpoint should be prepared to handle the following `eventType` values:

| Event Type                 | Description                                                                                    |
| -------------------------- | ---------------------------------------------------------------------------------------------- |
| `authorization_successful` | The user successfully authorized the transaction (e.g., 3DS)                                   |
| `authorization_failed`     | The user failed authorization or the bank denied the request                                   |
| `cancelled`                | You have decided to cancel the transaction after a successful authorization and before capture |
| `capture_initiated`        | The system has started the process of capturing the authorized funds                           |
| `capture_successful`       | The funds have been successfully captured. Transaction is complete                             |
| `capture_failed`           | The capture process failed after a successful authorization                                    |

### Notification Payload Structure

Notifications are sent as a `POST` request with a JSON body.

| Field                 | Type    | Description                                                                                         |
| --------------------- | ------- | --------------------------------------------------------------------------------------------------- |
| eventType             | string  | The specific event type being reported (e.g., `capture_successful`)                                 |
| timestamp             | string  | Notification timestamp                                                                              |
| data.txnId            | string  | The unique transaction ID generated by Coda                                                         |
| data.orderId          | string  | The merchant-side unique identifier for the order                                                   |
| data.amountValue      | decimal | The final amount charged to the user in the billing currency                                        |
| data.amountCurrency   | string  | The ISO 4217 currency code for the charged amount (e.g., `MYR`)                                     |
| data.originalValue    | decimal | The original price of the item before any currency conversions                                      |
| data.originalCurrency | string  | The ISO 4217 currency code for the original price                                                   |
| data.resultCode       | integer | Status code of the transaction. `0` indicates success; other values indicate errors.                |
| data.resultDesc       | string  | A human-readable description of the transaction result.                                             |
| data.shopper          | object  | An object containing shopper details such as ID, email, or partner-specific identifiers.            |
| data.paymentMethod    | object  | Information regarding the payment instrument used (e.g., card type, last four digits).              |
| data.transactionData  | object  | A flexible object containing supplementary metadata specific to the transaction or payment channel. |

### Successful Notification Example

The following example shows a `CAPTURE_SUCCESSFUL` notification, indicating that the funds have been successfully captured:

```json
{
  "eventType": "capture_successful",
  "timestamp": "1776084951007",
  "data": {
    "txnId": "7760845022800001729",
    "orderId": "12321312321314",
    "amountValue": 1,
    "amountCurrency": "EUR",
    "originalValue": 1,
    "originalCurrency": "EUR",
    "resultCode": 0,
    "shopper": {},
    "paymentMethod": {},
    "transactionData": {}
  }
}
```

### Failed Notification Example

When an event like `authorization_failed`, `capture_failed` or `cancelled` occurs, the `resultCode` will contain a non-zero value.

```json
{
  "eventType": "cancelled",
  "timestamp": "1776085975305",
  "data": {
    "txnId": "7760859208170001746",
    "orderId": "12321312321314",
    "amountValue": 1,
    "amountCurrency": "EUR",
    "originalValue": 1,
    "originalCurrency": "EUR",
    "resultCode": 221,
    "shopper": {},
    "paymentMethod": {},
    "transactionData": {}
  }
}
```

> You can find the full list of error codes and their meanings available [here](/coda-public-technical-documentation/codapay/error-codes.md).

### Notification Signature Verification

Every POST notification includes two headers that allow you to verify the request originated from Codapay and has not been tampered with:

| Header           | Description                                                                        |
| ---------------- | ---------------------------------------------------------------------------------- |
| `X-Request-Time` | The timestamp of the request, represented as epoch milliseconds (string of digits) |
| `X-Signature`    | HMAC-SHA512 hex digest computed by Codapay using your merchant secret key          |

#### How It Works

Codapay and your server both agree on a single UTF-8 string to sign:

```
X-Request-Time + "." + raw POST body (UTF-8)
```

The HMAC-SHA512 algorithm is applied to this string using your merchant **secret key**, producing a **128-character lowercase hexadecimal** digest.

Codapay sends this digest in the `X-Signature` header. Your server must recompute the same hex digest and compare it to the received value.

#### Validation Steps

1. Read the `X-Request-Time` and `X-Signature` headers from the incoming request.
2. Read the **raw body** bytes of the request.
3. Build the signed string: `requestTime + "." + body` (interpreted as UTF-8).
4. Compute the expected signature: `HMAC-SHA512(secretKey, signedString)` → lowercase hex.
5. Compare `expectedHex` to the `X-Signature` header value using a **constant-time** comparison to prevent timing attacks. The comparison is case-insensitive.

If the values do not match, reject the request.

#### Example — Java

```java
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;

public class SignatureValidator {

    private static final String HMAC_SHA512 = "HmacSHA512";

    public static boolean validate(String secretKey, String requestTime, byte[] body, String xSignature) {
        String bodyUtf8 = new String(body, StandardCharsets.UTF_8);
        String text = requestTime + "." + bodyUtf8;
        String expected = hmacSha512Hex(secretKey, text);
        return MessageDigest.isEqual(
            expected.toLowerCase().getBytes(),
            xSignature.toLowerCase().getBytes()
        );
    }

    private static String hmacSha512Hex(String secretKey, String text) {
        try {
            Mac mac = Mac.getInstance(HMAC_SHA512);
            mac.init(new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8), HMAC_SHA512));
            byte[] raw = mac.doFinal(text.getBytes(StandardCharsets.UTF_8));
            return HEX.formatHex(raw);
        } catch (Exception e) {
            throw new IllegalStateException("HMAC-SHA512 not available", e);
        }
    }
}
```

### Responding to Notifications

To acknowledge receipt of the notification, your server must respond with a `200 OK` and the following JSON body:

```json
{
  "ResultCode": 0
}
```
