# Guide to Alipay Webhooks: Features and Best Practices

Alipay notifications tell your systems about payment activity: a payment completes, a refund is processed, a dispute is raised. If you're integrating Alipay's global products, these async notifications are how you react to money movement without polling.

This guide covers how Alipay notifications work, how to verify the RSA signature (this is asymmetric crypto, verified with Alipay's public key), how the legacy scheme differs, and the best practices for production.

## What are Alipay webhooks?

Alipay's modern global products (Antom / Alipay+, the AMS API) send async notifications signed with asymmetric RSA (`algorithm=RSA256`, i.e. SHA256withRSA). Each request carries a `Signature` header plus `Client-Id` and `Request-Time` headers, and you verify with Alipay's public key from the dashboard.

There's an important caveat up front: Alipay has more than one signing scheme. This guide covers the Antom / Alipay+ header-RSA scheme (what the modern products and the Hookdeck source use). The legacy openapi/MAPI gateway uses a completely different form-param scheme, covered at the end. Confirm which your integration uses before verifying.

## Alipay webhook features

| Feature | Details |
| --- | --- |
| Configuration | Notify URL set per API call (`paymentNotifyUrl` / `refundNotifyUrl`) |
| Signature header | `Signature: algorithm=RSA256, keyVersion=1, signature=<sig>` |
| Other headers | `Client-Id`, `Request-Time` |
| Signature scheme | SHA256withRSA over `POST <path>\n<client-id>.<request-time>.<body>` |
| Verification key | Alipay's (Antom/Alipay+) public key from the Dashboard |
| Acknowledgement | HTTP 200 with `{"result":{"resultCode":"SUCCESS","resultStatus":"S","resultMessage":"Success"}}` |
| Retries | ~8 times over 24h if not acknowledged |
| SDK | `alipay-sdk` (targets the legacy gateway signing) |

## Common notifications

Alipay notification "names" are API method names, not a payload `type` field, the payload distinguishes via `notifyType` + `result.resultStatus`:

| Method | Notifies you about |
| --- | --- |
| `notifyPayment` | A payment result |
| `notifyRefund` | A refund result |
| `notifyCapture` | A capture result |
| `notifyDispute` | A dispute |
| `notifyAuthorization` | An authorization result |

Branch on `notifyType` and `result.resultStatus` (for example `S` for success) rather than a single event string.

## Setting up Alipay webhooks

The notify URL is set per API call (`paymentNotifyUrl`, `refundNotifyUrl`), not in a global dashboard. Download Alipay's (Antom/Alipay+) public key from the Dashboard to verify signatures. Acknowledge each notification with HTTP 200 and the success result body; if you don't, Antom retries roughly 8 times over 24 hours.

## Securing Alipay webhooks

### The modern Antom / Alipay+ header-RSA scheme

Each notification carries a `Signature` header (`algorithm=RSA256, keyVersion=1, signature=<sig>`), a `Client-Id`, and a `Request-Time`. To verify, reconstruct the signed content as `POST <path>\n<client-id>.<request-time>.<body>` (the HTTP method and path on line one, then client id, request time, and the raw body joined by dots), URL-decode and base64-decode the `signature` value, and verify with SHA256withRSA against Alipay's public key. Use the raw body directly, do not parse and reconstruct the JSON, or verification fails.

```javascript
const crypto = require("crypto");

const ALIPAY_PUBLIC_KEY = process.env.ALIPAY_PUBLIC_KEY; // PEM from the Dashboard

function parseSignature(header) {
  // "algorithm=RSA256, keyVersion=1, signature=<sig>"
  const m = /signature=([^,]+)/.exec(header || "");
  return m ? decodeURIComponent(m[1]) : "";
}

app.post("/webhook", express.raw({ type: "application/json" }), (req, res) => {
  const clientId = req.headers["client-id"];
  const requestTime = req.headers["request-time"];
  const signature = parseSignature(req.headers["signature"]);

  const content = `POST ${req.path}\n${clientId}.${requestTime}.${req.body.toString()}`;
  const verifier = crypto.createVerify("RSA-SHA256");
  verifier.update(content);

  if (!verifier.verify(ALIPAY_PUBLIC_KEY, Buffer.from(signature, "base64"))) {
    return res.sendStatus(401);
  }

  res.status(200).json({
    result: { resultCode: "SUCCESS", resultStatus: "S", resultMessage: "Success" },
  });
  processQueue.add(JSON.parse(req.body)); // process asynchronously
});

```

The same verification in Python (using `cryptography`):

```python
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives import hashes

def verify(method, path, client_id, request_time, raw_body, signature_b64, public_key):
    content = f"{method} {path}\n{client_id}.{request_time}.{raw_body.decode()}".encode()
    try:
        public_key.verify(signature_b64, content, padding.PKCS1v15(), hashes.SHA256())
        return True
    except Exception:
        return False

```

### The legacy openapi / MAPI scheme

The older gateway (`global.alipay.com`, `openapi.alipay.com`) is completely different: form-encoded parameters with `sign` and `sign_type=RSA2`, verified by removing `sign`/`sign_type`, sorting the remaining params A-Z, joining with `&`, and RSA2-verifying against Alipay's public key, then replying with the plain text `success`. If your integration is on this gateway, use that scheme instead.

## Alipay webhook limitations and pain points

### Three schemes under one brand

The Problem: Antom AMS, Alipay+ Acquirer, and the legacy openapi/MAPI gateway sign differently. Verifying with the wrong scheme fails, and it's not always obvious which one a merchant is on.

Why It Happens: Alipay's products evolved separately, each with its own signing.

Workarounds:

* Confirm your integration vintage and use the matching scheme (this guide's header-RSA for modern products, form-param RSA2 for legacy).

How Hookdeck Can Help: Hookdeck verifies the signature at the edge, so your application receives pre-verified notifications without untangling which Alipay scheme applies.

### The signed content is a request representation, not just the body

The Problem: The modern scheme signs `POST <path>\n<client-id>.<request-time>.<body>`, so you need the method, path, and headers, not just the body, and the raw body must be used verbatim.

Why It Happens: Antom signs a canonical request representation.

Workarounds:

* Reconstruct the exact string and use the raw body; don't reparse the JSON.

How Hookdeck Can Help: Hookdeck handles the canonical-string RSA verification at the edge, so your app doesn't reproduce it.

### Ack format and retries

The Problem: Alipay expects a specific success result body, not just a 200. An incorrect ack leads to ~8 retries over 24 hours, so the same notification arrives repeatedly.

Why It Happens: Antom keys retry behavior on the structured result acknowledgement.

Workarounds:

* Return the exact `{"result":{"resultCode":"SUCCESS",...}}` body, and dedupe/idempotently process retries.

How Hookdeck Can Help: Hookdeck acknowledges Alipay correctly and deduplicates, so retries don't double-process. See our [guide to webhook idempotency](/webhooks/guides/implement-webhook-idempotency).

### Method-name, not event-type, payloads

The Problem: There's no single event `type` string; you branch on the method (`notifyPayment`, ...) plus `notifyType` and `result.resultStatus`. Handlers expecting a flat event name mis-route.

Why It Happens: Alipay models notifications as API methods with result codes.

Workarounds:

* Branch on `notifyType` + `result.resultStatus`.

How Hookdeck Can Help: Hookdeck's transformations can normalize notifications into a consistent event shape for downstream handlers.

## Best practices

### Verify the header-RSA scheme with the raw body

Reconstruct `POST <path>\n<client-id>.<request-time>.<body>`, URL-decode and base64-decode the signature, and verify SHA256withRSA with Alipay's public key. Use the raw body.

### Return the exact success ack

Reply 200 with `{"result":{"resultCode":"SUCCESS","resultStatus":"S","resultMessage":"Success"}}` to stop retries.

### Confirm your scheme

Use the modern header-RSA scheme for Antom/Alipay+, or the legacy form-param RSA2 scheme for the openapi/MAPI gateway.

### Dedupe and confirm before fulfilling

Dedupe across the ~8 retries, and re-verify the order amount via the API before releasing value.

## Conclusion

Alipay's modern notifications are signed with SHA256withRSA over `POST <path>\n<client-id>.<request-time>.<body>`, verified with Alipay's public key, and acknowledged with a structured success result. Confirm which of Alipay's schemes you're on (modern header-RSA vs legacy form-param RSA2), verify against the raw body, return the exact ack, and dedupe across retries.

[Hookdeck](https://hookdeck.com) verifies the RSA signature, deduplicates, and durably queues every notification at the edge, so your app only ever processes verified, unique payment events.

[Get started with Hookdeck](https://dashboard.hookdeck.com/signup) for free and handle Alipay webhooks reliably in minutes.