> ## Documentation Index
> Fetch the complete documentation index at: https://developer.gcashier.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Receiving Webhook Notifications from Gcashier Pay

> Gcashier Pay POSTs encrypted webhook callbacks to your callbackUrl for every async event, using the same envelope format as regular API responses.

Many Gcashier Pay operations are asynchronous: the platform accepts your request immediately (returning `respCode: S00001`) and then completes the work in the background. When the operation finishes, Gcashier Pay pushes the result to a webhook endpoint on your server. You register this endpoint by including a `callbackUrl` in the relevant API request body. Understanding how to receive, decrypt, and verify these callbacks is essential for building a reliable integration.

## How Webhooks Work

Gcashier Pay sends webhook notifications as **HTTPS POST requests** to your `callbackUrl`. The POST body uses exactly the same four-field encrypted envelope as a standard API response:

```json theme={null}
{
  "merchantNo": "M123456789",
  "jsonEnc":    "<Base64 AES ciphertext of the notification payload>",
  "keyEnc":     "<HEX RSA-encrypted AES session key>",
  "sign":       "<HEX SHA1withRSA signature>"
}
```

Decrypt and verify the callback using the same process described in [Encryption & Signing](/concepts/encryption-signing): recover `SK` from `keyEnc` using your private key, decrypt `jsonEnc` with `SK`, then verify `sign` against the plaintext using Gcashier Pay's public key.

<Warning>
  Always verify the `sign` field before processing any callback. An invalid signature means the message did not originate from Gcashier Pay or has been tampered with. Discard such messages and log the event for investigation.
</Warning>

## Responding to a Webhook

Your endpoint must return an **HTTP 200** status code to acknowledge successful receipt of the callback. If Gcashier Pay does not receive a 200 response — due to a network error, timeout, or any non-200 status code from your server — it will retry the notification. Implement your handler to be **idempotent**: processing the same callback more than once must not cause duplicate actions (for example, duplicate fund movements or double-confirmed orders).

<Note>
  If you never receive a callback for an operation (for example, due to a prolonged outage), use the corresponding query API endpoint to check the operation's status directly. Do not rely solely on callbacks for critical state transitions.
</Note>

## Webhook Event Codes

Each callback carries a `tradeCode` in its decrypted `head` that identifies the type of event. The table below lists all supported notification codes:

| Trade Code | Event                               |
| ---------- | ----------------------------------- |
| `sp3101`   | Merchant access (onboarding) result |
| `sp3102`   | Virtual account (VA) opening result |
| `sp3103`   | Trade receipt notification          |
| `sp3104`   | Trade order application result      |
| `sp3105`   | Flow-order association result       |
| `sp3201`   | FX trading result                   |
| `sp3301`   | RMB payment result                  |
| `sp3302`   | International remittance result     |
| `sp3303`   | Withdrawal result                   |
| `sp3304`   | Internal transfer result            |
| `sp3401`   | Payee registration result           |

## Setting Your callbackUrl

You register a callback URL per-request, not as a global setting. Include a `callbackUrl` field in the `body` of any request that triggers an asynchronous operation. Gcashier Pay will POST the result notification to that URL when the operation completes.

```json theme={null}
{
  "head": {
    "version":   "1.0.0",
    "tradeType": "00",
    "tradeTime": "1714000000",
    "tradeCode": "sp1301",
    "language":  "en"
  },
  "body": {
    "orderNo":     "ORD-2024-00001",
    "currency":    "USD",
    "amount":      "100.00",
    "callbackUrl": "https://yoursite.com/webhooks/gcashier"
  }
}
```

<Tip>
  Use a unique, versioned path for your webhook endpoint (e.g. `/webhooks/gcashier/v1`) so you can roll out handler changes without disrupting in-flight notifications. Ensure your endpoint is publicly reachable over HTTPS — Gcashier Pay cannot deliver callbacks to private or localhost URLs.
</Tip>

## Webhook Processing Checklist

Use the following checklist when implementing your webhook handler:

<Steps>
  <Step title="Parse the outer envelope">
    Read the POST body as JSON and extract `merchantNo`, `jsonEnc`, `keyEnc`, and `sign`.
  </Step>

  <Step title="Decrypt the payload">
    HEX-decode `keyEnc`, RSA-decrypt with your private key to recover the AES session key `SK`. Base64-decode `jsonEnc` and decrypt with `SK` (AES/ECB/PKCS7Padding) to obtain the inner JSON plaintext.
  </Step>

  <Step title="Verify the signature">
    HEX-decode `sign` and verify it against the inner JSON plaintext using Gcashier Pay's public key (SHA1withRSA). Reject and log any message that fails verification.
  </Step>

  <Step title="Inspect the tradeCode">
    Read `head.tradeCode` from the decrypted JSON to determine which event type you received, then route the `body` to the appropriate handler.
  </Step>

  <Step title="Process idempotently">
    Check whether you have already processed this event (for example, by recording the order or transaction number). If you have, skip reprocessing and return 200 immediately.
  </Step>

  <Step title="Return HTTP 200">
    Respond with a 200 status code. Any other status or a connection timeout will trigger a retry from Gcashier Pay.
  </Step>
</Steps>
